Quellcode durchsuchen

Fixed #21247 -- Made method_decorator play nicely with descriptors

When a method decorator was used in conjunction with a decorator
implemented as a descriptor, method_decorator did not correctly respect
the method binding.

Thanks for Graham Dumpleton for the report and initial patch.
Marc Tamlyn vor 11 Jahren
Ursprung
Commit
80cd64ee17
2 geänderte Dateien mit 38 neuen und 2 gelöschten Zeilen
  1. 1 1
      django/utils/decorators.py
  2. 37 1
      tests/decorators/tests.py

+ 1 - 1
django/utils/decorators.py

@@ -22,7 +22,7 @@ def method_decorator(decorator):
         def _wrapper(self, *args, **kwargs):
             @decorator
             def bound_func(*args2, **kwargs2):
-                return func(self, *args2, **kwargs2)
+                return func.__get__(self, type(self))(*args2, **kwargs2)
             # bound_func has the signature that 'decorator' expects i.e.  no
             # 'self' argument, but it is a closure over self so it can call
             # 'func' correctly.

+ 37 - 1
tests/decorators/tests.py

@@ -232,9 +232,45 @@ class MethodDecoratorTests(TestCase):
             def method(self):
                 return True
 
-        # t = Test()
         self.assertEqual(Test().method(), False)
 
+    def test_descriptors(self):
+
+        def original_dec(wrapped):
+            def _wrapped(arg):
+                return wrapped(arg)
+
+            return _wrapped
+
+        method_dec = method_decorator(original_dec)
+
+        class bound_wrapper(object):
+            def __init__(self, wrapped):
+                self.wrapped = wrapped
+                self.__name__ = wrapped.__name__
+
+            def __call__(self, arg):
+                return self.wrapped(arg)
+
+            def __get__(self, instance, owner):
+                return self
+
+        class descriptor_wrapper(object):
+            def __init__(self, wrapped):
+                self.wrapped = wrapped
+                self.__name__ = wrapped.__name__
+
+            def __get__(self, instance, owner):
+                return bound_wrapper(self.wrapped.__get__(instance, owner))
+
+        class Test(object):
+            @method_dec
+            @descriptor_wrapper
+            def method(self, arg):
+                return arg
+
+        self.assertEqual(Test().method(1), 1)
+
 
 class XFrameOptionsDecoratorsTests(TestCase):
     """