Accessing original decorated function for test pur

2020-03-03 10:11发布

问题:

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?

回答1:

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.