Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

Here is a code which recursively searches the original function which is decorated at many levels. The logic behind this is same as the answer mentioned by @Martin

def get_original_decorated_function(deco_func, org_func_name= None):
    if not deco_func.func_closure:
        if deco_func.__name__ == org_func_name:
            return deco_func
        else:
            return None
    for closure in deco_func.func_closure:
        func = closure.cell_contents
        if func.__name__ == org_func_name:
            # This is the original function name
            return func
        else:
            # Recursively call the intermediate function
            return get_original_decorated_function(func, org_func_name)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top