Commit 5864834f authored by Luke Plant's avatar Luke Plant
Browse files

Fixed a bug with method_decorator not preserving the attributes of the wrapped...

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
parent df0bb3c4
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -11,15 +11,28 @@ def method_decorator(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 _wrapper(self, *args, **kwargs):
            @decorator
            def bound_func(*args2, **kwargs2):
                return func(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.
            return decorator(bound_func)(*args, **kwargs)
        return wraps(func)(_wrapper)
            return bound_func(*args, **kwargs)
        # 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)
    # Change the name to aid debugging.
    _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
+47 −1
Original line number Diff line number Diff line
@@ -126,14 +126,60 @@ def simple_dec(func):
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):
    """
    Tests for method_decorator
    """
    def test_method_decorator(self):
    def test_preserve_signature(self):
        class Test(object):
            @simple_dec_m
            def say(self, arg):
                return arg

        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')