Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing original decorated function for test purposes

I'm using a decorator(@render_to from the django_annoying package) in a view function.

But the thing is that I wanted to get the original dict that is returned by the view function for test purposes, instead of the HttpResponse object that the decorator returns.

The decorator uses @wraps (from functools).

If there is no way to access this, then do you have any idea of how to test this?

like image 682
Arruda Avatar asked Dec 11 '22 17:12

Arruda


1 Answers

The wrapped function will be available as a function closure cell. Which cell exactly depends on how many closure variables there are.

For a simple wrapper where the only closure variable is the function-to-wrap, it'll be the first one:

wrapped = decorated.func_closure[0].cell_contents

but you may have to inspect all func_closure values.

Demo using the functools.wraps() example decorator:

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print 'Calling decorated function'
...         return f(*args, **kwds)
...     return wrapper
... 
>>> @my_decorator
... def example():
...     """Docstring"""
...     print 'Called example function'
... 
>>> example
<function example at 0x107ddfaa0>
>>> example.func_closure
(<cell at 0x107de3d70: function object at 0x107dc3b18>,)
>>> example.func_closure[0].cell_contents
<function example at 0x107dc3b18>
>>> example()
Calling decorated function
Called example function
>>> example.func_closure[0].cell_contents()
Called example function

Looking at the source code for @render_to you don't have to worry about this though; the wrapped function will be stored in the first closure slot, guaranteed.

If this was Python 3 instead, the wrapped function can be accessed with the __wrapped__ attribute instead:

>>> example.__wrapped__
<function example at 0x103329050>

And if you had access to the decorator code itself, you can easily add that same reference in Python 2 code too:

def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        # implementation

    wrapper.__wrapped__ = f
    return wrapper

making introspection just that little bit easier.

like image 89
Martijn Pieters Avatar answered Jan 16 '23 03:01

Martijn Pieters