Fixed a bug with method_decorator not preserving the attributes of the wrapped method, which is important for decorators like csrf_exempt
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14311 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
df0bb3c463
commit
5864834fa5
|
@ -11,15 +11,28 @@ def method_decorator(decorator):
|
||||||
"""
|
"""
|
||||||
Converts a function decorator into a method decorator
|
Converts a function decorator into a method decorator
|
||||||
"""
|
"""
|
||||||
|
# 'func' is a function at the time it is passed to _dec, but will eventually
|
||||||
|
# be a method of the class it is defined it.
|
||||||
def _dec(func):
|
def _dec(func):
|
||||||
def _wrapper(self, *args, **kwargs):
|
def _wrapper(self, *args, **kwargs):
|
||||||
|
@decorator
|
||||||
def bound_func(*args2, **kwargs2):
|
def bound_func(*args2, **kwargs2):
|
||||||
return func(self, *args2, **kwargs2)
|
return func(self, *args2, **kwargs2)
|
||||||
# bound_func has the signature that 'decorator' expects i.e. no
|
# bound_func has the signature that 'decorator' expects i.e. no
|
||||||
# 'self' argument, but it is a closure over self so it can call
|
# 'self' argument, but it is a closure over self so it can call
|
||||||
# 'func' correctly.
|
# 'func' correctly.
|
||||||
return decorator(bound_func)(*args, **kwargs)
|
return bound_func(*args, **kwargs)
|
||||||
return wraps(func)(_wrapper)
|
# In case 'decorator' adds attributes to the function it decorates, we
|
||||||
|
# want to copy those. We don't have access to bound_func in this scope,
|
||||||
|
# but we can cheat by using it on a dummy function.
|
||||||
|
@decorator
|
||||||
|
def dummy(*args, **kwargs):
|
||||||
|
pass
|
||||||
|
update_wrapper(_wrapper, dummy)
|
||||||
|
# Need to preserve any existing attributes of 'func', including the name.
|
||||||
|
update_wrapper(_wrapper, func)
|
||||||
|
|
||||||
|
return _wrapper
|
||||||
update_wrapper(_dec, decorator)
|
update_wrapper(_dec, decorator)
|
||||||
# Change the name to aid debugging.
|
# Change the name to aid debugging.
|
||||||
_dec.__name__ = 'method_decorator(%s)' % decorator.__name__
|
_dec.__name__ = 'method_decorator(%s)' % decorator.__name__
|
||||||
|
|
|
@ -126,14 +126,60 @@ def simple_dec(func):
|
||||||
simple_dec_m = method_decorator(simple_dec)
|
simple_dec_m = method_decorator(simple_dec)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing method_decorator, two decorators that add an attribute to the function
|
||||||
|
def myattr_dec(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
wrapper.myattr = True
|
||||||
|
return wraps(func)(wrapper)
|
||||||
|
|
||||||
|
myattr_dec_m = method_decorator(myattr_dec)
|
||||||
|
|
||||||
|
|
||||||
|
def myattr2_dec(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
wrapper.myattr2 = True
|
||||||
|
return wraps(func)(wrapper)
|
||||||
|
|
||||||
|
myattr2_dec_m = method_decorator(myattr2_dec)
|
||||||
|
|
||||||
|
|
||||||
class MethodDecoratorTests(TestCase):
|
class MethodDecoratorTests(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests for method_decorator
|
Tests for method_decorator
|
||||||
"""
|
"""
|
||||||
def test_method_decorator(self):
|
def test_preserve_signature(self):
|
||||||
class Test(object):
|
class Test(object):
|
||||||
@simple_dec_m
|
@simple_dec_m
|
||||||
def say(self, arg):
|
def say(self, arg):
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
self.assertEqual("test:hello", Test().say("hello"))
|
self.assertEqual("test:hello", Test().say("hello"))
|
||||||
|
|
||||||
|
def test_preserve_attributes(self):
|
||||||
|
# Sanity check myattr_dec and myattr2_dec
|
||||||
|
@myattr_dec
|
||||||
|
@myattr2_dec
|
||||||
|
def func():
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(getattr(func, 'myattr', False), True)
|
||||||
|
self.assertEqual(getattr(func, 'myattr2', False), True)
|
||||||
|
|
||||||
|
# Now check method_decorator
|
||||||
|
class Test(object):
|
||||||
|
@myattr_dec_m
|
||||||
|
@myattr2_dec_m
|
||||||
|
def method(self):
|
||||||
|
"A method"
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(getattr(Test().method, 'myattr', False), True)
|
||||||
|
self.assertEqual(getattr(Test().method, 'myattr2', False), True)
|
||||||
|
|
||||||
|
self.assertEqual(getattr(Test.method, 'myattr', False), True)
|
||||||
|
self.assertEqual(getattr(Test.method, 'myattr2', False), True)
|
||||||
|
|
||||||
|
self.assertEqual(Test.method.__doc__, 'A method')
|
||||||
|
self.assertEqual(Test.method.im_func.__name__, 'method')
|
||||||
|
|
Loading…
Reference in New Issue