Refs #29253 -- Fixed method_decorator() crash if decorator sets a new attribute.
Regression in fdc936c913
.
This commit is contained in:
parent
f52b026168
commit
f434f5b84f
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# For backwards compatibility in Django 2.0.
|
# For backwards compatibility in Django 2.0.
|
||||||
from contextlib import ContextDecorator # noqa
|
from contextlib import ContextDecorator # noqa
|
||||||
from functools import WRAPPER_ASSIGNMENTS, update_wrapper, wraps
|
from functools import WRAPPER_ASSIGNMENTS, partial, update_wrapper, wraps
|
||||||
|
|
||||||
|
|
||||||
class classonlymethod(classmethod):
|
class classonlymethod(classmethod):
|
||||||
|
@ -36,8 +36,10 @@ def _multi_decorate(decorators, method):
|
||||||
|
|
||||||
def _wrapper(self, *args, **kwargs):
|
def _wrapper(self, *args, **kwargs):
|
||||||
# bound_method has the signature that 'decorator' expects i.e. no
|
# bound_method has the signature that 'decorator' expects i.e. no
|
||||||
# 'self' argument.
|
# 'self' argument, but it's a closure over self so it can call
|
||||||
bound_method = method.__get__(self, type(self))
|
# 'func'. Also, wrap method.__get__() in a function because new
|
||||||
|
# attributes can't be set on bound method objects, only on functions.
|
||||||
|
bound_method = partial(method.__get__(self, type(self)))
|
||||||
for dec in decorators:
|
for dec in decorators:
|
||||||
bound_method = dec(bound_method)
|
bound_method = dec(bound_method)
|
||||||
return bound_method(*args, **kwargs)
|
return bound_method(*args, **kwargs)
|
||||||
|
|
|
@ -271,6 +271,21 @@ class MethodDecoratorTests(SimpleTestCase):
|
||||||
self.assertEqual(Test.method.__doc__, 'A method')
|
self.assertEqual(Test.method.__doc__, 'A method')
|
||||||
self.assertEqual(Test.method.__name__, 'method')
|
self.assertEqual(Test.method.__name__, 'method')
|
||||||
|
|
||||||
|
def test_new_attribute(self):
|
||||||
|
"""A decorator that sets a new attribute on the method."""
|
||||||
|
def decorate(func):
|
||||||
|
func.x = 1
|
||||||
|
return func
|
||||||
|
|
||||||
|
class MyClass:
|
||||||
|
@method_decorator(decorate)
|
||||||
|
def method(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
obj = MyClass()
|
||||||
|
self.assertEqual(obj.method.x, 1)
|
||||||
|
self.assertIs(obj.method(), True)
|
||||||
|
|
||||||
def test_bad_iterable(self):
|
def test_bad_iterable(self):
|
||||||
decorators = {myattr_dec_m, myattr2_dec_m}
|
decorators = {myattr_dec_m, myattr2_dec_m}
|
||||||
msg = "'set' object is not subscriptable"
|
msg = "'set' object is not subscriptable"
|
||||||
|
|
Loading…
Reference in New Issue