ビュー関数でデコレータ(パッケージ@render_to
から)を使用しています。django_annoying
HttpResponse
しかし、問題は、デコレータが返すオブジェクトではなく、テスト目的でビュー関数によって返される元の dict を取得したかったことです。
デコレータは@wraps
(from functools
) を使用します。
これにアクセスする方法がない場合、これをテストする方法はありますか?
ビュー関数でデコレータ(パッケージ@render_to
から)を使用しています。django_annoying
HttpResponse
しかし、問題は、デコレータが返すオブジェクトではなく、テスト目的でビュー関数によって返される元の dict を取得したかったことです。
デコレータは@wraps
(from functools
) を使用します。
これにアクセスする方法がない場合、これをテストする方法はありますか?
ラップされた関数は、関数クロージャ セルとして使用できます。どのセルが正確には、クロージャ変数がいくつあるかによって異なります。
唯一のクロージャ変数がラップする関数である単純なラッパーの場合、それは最初のものになります:
wrapped = decorated.func_closure[0].cell_contents
ただし、すべての値を検査する必要がある場合がありfunc_closure
ます。
functools.wraps()
サンプル デコレータを使用したデモ:
>>> 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
ただし、ソース コードを@render_to
見ても、これについて心配する必要はありません。ラップされた関数は、保証された最初のクロージャ スロットに格納されます。
これが代わりに Python 3 であった場合、代わりに__wrapped__
属性を使用してラップされた関数にアクセスできます。
>>> example.__wrapped__
<function example at 0x103329050>
また、デコレータ コード自体にアクセスできる場合は、同じ参照を Python 2 コードにも簡単に追加できます。
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
# implementation
wrapper.__wrapped__ = f
return wrapper
イントロスペクションを少しだけ簡単にします。
これは、多くのレベルで装飾された元の関数を再帰的に検索するコードです。この背後にあるロジックは、@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)