expected doc return httpresponsehtmlbodydummybodyhtml fullydecorated anything expected dict def composefunctions composef gargs kwargs fgargs kwargs functions listreversedfunctions def innerargs kwargs result functions0args kwargs for f in functions1 result fresult return result return inner fulldecorator compose django views decorators http requirehttpmethodsget requireget requirepost requiresafe conditionlambda r none lambda r none django views decorators vary varyonheadersacceptlanguage varyoncookie django views decorators cache cachepage60 15 cachecontrolprivatetrue nevercache django contrib auth decorators apply userpassestest twice to check 9474 userpassestestlambda u true loginrequired permissionrequiredchangeworld django contrib admin views decorators staffmemberrequired django utils functional keeplazyhttpresponse keeplazytext lazy django utils safestring marksafe fullydecorated fulldecoratorfullydecorated class decoratorstesttestcase def testattributesself self assertequalfullydecorated name fullydecorated self assertequalfullydecorated doc expected doc self assertequalfullydecorated dictanything expected dict def testuserpassestestcompositionself def test1user user decoratorsapplied appendtest1 return true def test2user user decoratorsapplied appendtest2 return true def callbackrequest return request user decoratorsapplied callback userpassestesttest1callback callback userpassestesttest2callback class dummyuser pass class dummyrequest pass request dummyrequest request user dummyuser request user decoratorsapplied response callbackrequest self assertequalresponse test2 test1 for testing methoddecorator a decorator that assumes a single argument we will get type arguments if there is a mismatch in the number of arguments def simpledecfunc wrapsfunc def wrapperarg return functest arg return wrapper simpledecm methoddecoratorsimpledec for testing methoddecorator two decorators that add an attribute to the function def myattrdecfunc def wrapperargs kwargs return funcargs kwargs wrapper myattr true return wrapper myattrdecm methoddecoratormyattrdec def myattr2decfunc def wrapperargs kwargs return funcargs kwargs wrapper myattr2 true return wrapper myattr2decm methoddecoratormyattr2dec class clsdec def initself myattr self myattr myattr def callself f def wrapper return f and self myattr return updatewrapperwrapper f class methoddecoratortestssimpletestcase def testpreservesignatureself class test simpledecm def sayself arg return arg self assertequaltest hello test sayhello def testpreserveattributesself sanity check myattrdec and myattr2dec myattrdec def func pass self assertisgetattrfunc myattr false true myattr2dec def func pass self assertisgetattrfunc myattr2 false true myattrdec myattr2dec def func pass self assertisgetattrfunc myattr false true self assertisgetattrfunc myattr2 false false decorate using methoddecorator on the method class testplain myattrdecm myattr2decm def methodself a method pass decorate using methoddecorator on both the class and the method the decorators applied to the methods are applied before the ones applied to the class methoddecoratormyattrdecm method class testmethodandclass methoddecoratormyattr2decm def methodself a method pass decorate using an iterable of function decorators methoddecoratormyattrdec myattr2dec method class testfunctioniterable def methodself a method pass decorate using an iterable of method decorators decorators myattrdecm myattr2decm methoddecoratordecorators method class testmethoditerable def methodself a method pass tests testplain testmethodandclass testfunctioniterable testmethoditerable for test in tests with self subtesttesttest self assertisgetattrtest method myattr false true self assertisgetattrtest method myattr2 false true self assertisgetattrtest method myattr false true self assertisgetattrtest method myattr2 false true self assertequaltest method doc a method self assertequaltest method name method def testnewattributeself test for argumented decorator methoddecorator can be used to decorate a class and its methods methoddecorator can accept a tuple of decorators the order should be consistent with the usual order in which decorators are applied e g addexclamationmark addquestionmark def func methoddecorator on a noncallable attribute raises an error methoddecorator on a nonexistent method raises an error methoddecorator preserves wrapper assignments funcname none funcmodule none def decoratorfunc wrapsfunc def innerargs kwargs nonlocal funcname funcmodule funcname getattrfunc name none funcmodule getattrfunc module none return funcargs kwargs return inner class test methoddecoratordecorator def methodself return tests test method self assertequalfuncname method self assertisnotnonefuncmodule expected __doc__ compose f g args kwargs f g args kwargs django views decorators http django views decorators vary django views decorators cache django contrib auth decorators apply user_passes_test twice to check 9474 django contrib admin views decorators django utils functional django utils safestring built in decorators set certain attributes of the wrapped function the user_passes_test decorator can be applied multiple times 9474 for testing method_decorator a decorator that assumes a single argument we will get type arguments if there is a mismatch in the number of arguments for testing method_decorator two decorators that add an attribute to the function tests for method_decorator sanity check myattr_dec and myattr2_dec decorate using method_decorator on the method decorate using method_decorator on both the class and the method the decorators applied to the methods are applied before the ones applied to the class decorate using an iterable of function decorators decorate using an iterable of method decorators a decorator that sets a new attribute on the method test for argumented decorator method_decorator can be used to decorate a class and its methods method_decorator can accept a tuple of decorators the order should be consistent with the usual order in which decorators are applied e g add_exclamation_mark add_question_mark def func method_decorator on a non callable attribute raises an error method_decorator on a nonexistent method raises an error method_decorator preserves wrapper assignments
from functools import update_wrapper, wraps
from unittest import TestCase

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import (
    login_required,
    permission_required,
    user_passes_test,
)
from django.http import HttpResponse
from django.test import SimpleTestCase
from django.utils.decorators import method_decorator
from django.utils.functional import keep_lazy, keep_lazy_text, lazy
from django.utils.safestring import mark_safe
from django.views.decorators.cache import cache_control, cache_page, never_cache
from django.views.decorators.http import (
    condition,
    require_GET,
    require_http_methods,
    require_POST,
    require_safe,
)
from django.views.decorators.vary import vary_on_cookie, vary_on_headers


def fully_decorated(request):
    
    return HttpResponse("<html><body>dummy</body></html>")


fully_decorated.anything = "Expected __dict__"


def compose(*functions):
    
    functions = list(reversed(functions))

    def _inner(*args, **kwargs):
        result = functions[0](*args, **kwargs)
        for f in functions[1:]:
            result = f(result)
        return result

    return _inner


full_decorator = compose(
    
    require_http_methods(["GET"]),
    require_GET,
    require_POST,
    require_safe,
    condition(lambda r: None, lambda r: None),
    
    vary_on_headers("Accept-language"),
    vary_on_cookie,
    
    cache_page(60 * 15),
    cache_control(private=True),
    never_cache,
    
    
    user_passes_test(lambda u: True),
    login_required,
    permission_required("change_world"),
    
    staff_member_required,
    
    keep_lazy(HttpResponse),
    keep_lazy_text,
    lazy,
    
    mark_safe,
)

fully_decorated = full_decorator(fully_decorated)


class DecoratorsTest(TestCase):
    def test_attributes(self):
        
        self.assertEqual(fully_decorated.__name__, "fully_decorated")
        self.assertEqual(fully_decorated.__doc__, "Expected __doc__")
        self.assertEqual(fully_decorated.__dict__["anything"], "Expected __dict__")

    def test_user_passes_test_composition(self):
        

        def test1(user):
            user.decorators_applied.append("test1")
            return True

        def test2(user):
            user.decorators_applied.append("test2")
            return True

        def callback(request):
            return request.user.decorators_applied

        callback = user_passes_test(test1)(callback)
        callback = user_passes_test(test2)(callback)

        class DummyUser:
            pass

        class DummyRequest:
            pass

        request = DummyRequest()
        request.user = DummyUser()
        request.user.decorators_applied = []
        response = callback(request)

        self.assertEqual(response, ["test2", "test1"])




def simple_dec(func):
    @wraps(func)
    def wrapper(arg):
        return func("test:" + arg)

    return wrapper


simple_dec_m = method_decorator(simple_dec)



def myattr_dec(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    wrapper.myattr = True
    return wrapper


myattr_dec_m = method_decorator(myattr_dec)


def myattr2_dec(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    wrapper.myattr2 = True
    return wrapper


myattr2_dec_m = method_decorator(myattr2_dec)


class ClsDec:
    def __init__(self, myattr):
        self.myattr = myattr

    def __call__(self, f):
        def wrapper():
            return f() and self.myattr

        return update_wrapper(wrapper, f)


class MethodDecoratorTests(SimpleTestCase):
    

    def test_preserve_signature(self):
        class Test:
            @simple_dec_m
            def say(self, arg):
                return arg

        self.assertEqual("test:hello", Test().say("hello"))

    def test_preserve_attributes(self):
        
        @myattr_dec
        def func():
            pass

        self.assertIs(getattr(func, "myattr", False), True)

        @myattr2_dec
        def func():
            pass

        self.assertIs(getattr(func, "myattr2", False), True)

        @myattr_dec
        @myattr2_dec
        def func():
            pass

        self.assertIs(getattr(func, "myattr", False), True)
        self.assertIs(getattr(func, "myattr2", False), False)

        
        class TestPlain:
            @myattr_dec_m
            @myattr2_dec_m
            def method(self):
                "A method"
                pass

        
        
        
        @method_decorator(myattr_dec_m, "method")
        class TestMethodAndClass:
            @method_decorator(myattr2_dec_m)
            def method(self):
                "A method"
                pass

        
        @method_decorator((myattr_dec, myattr2_dec), "method")
        class TestFunctionIterable:
            def method(self):
                "A method"
                pass

        
        decorators = (myattr_dec_m, myattr2_dec_m)

        @method_decorator(decorators, "method")
        class TestMethodIterable:
            def method(self):
                "A method"
                pass

        tests = (
            TestPlain,
            TestMethodAndClass,
            TestFunctionIterable,
            TestMethodIterable,
        )
        for Test in tests:
            with self.subTest(Test=Test):
                self.assertIs(getattr(Test().method, "myattr", False), True)
                self.assertIs(getattr(Test().method, "myattr2", False), True)
                self.assertIs(getattr(Test.method, "myattr", False), True)
                self.assertIs(getattr(Test.method, "myattr2", False), True)
                self.assertEqual(Test.method.__doc__, "A method")
                self.assertEqual(Test.method.__name__, "method")

    def test_new_attribute(self):
        

        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):
        decorators = {myattr_dec_m, myattr2_dec_m}
        msg = "'set' object is not subscriptable"
        with self.assertRaisesMessage(TypeError, msg):

            @method_decorator(decorators, "method")
            class TestIterable:
                def method(self):
                    "A method"
                    pass

    
    def test_argumented(self):
        class Test:
            @method_decorator(ClsDec(False))
            def method(self):
                return True

        self.assertIs(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:
            def __init__(self, wrapped):
                self.wrapped = wrapped
                self.__name__ = wrapped.__name__

            def __call__(self, arg):
                return self.wrapped(arg)

            def __get__(self, instance, cls=None):
                return self

        class descriptor_wrapper:
            def __init__(self, wrapped):
                self.wrapped = wrapped
                self.__name__ = wrapped.__name__

            def __get__(self, instance, cls=None):
                return bound_wrapper(self.wrapped.__get__(instance, cls))

        class Test:
            @method_dec
            @descriptor_wrapper
            def method(self, arg):
                return arg

        self.assertEqual(Test().method(1), 1)

    def test_class_decoration(self):
        

        def deco(func):
            def _wrapper(*args, **kwargs):
                return True

            return _wrapper

        @method_decorator(deco, name="method")
        class Test:
            def method(self):
                return False

        self.assertTrue(Test().method())

    def test_tuple_of_decorators(self):
        

        def add_question_mark(func):
            def _wrapper(*args, **kwargs):
                return func(*args, **kwargs) + "?"

            return _wrapper

        def add_exclamation_mark(func):
            def _wrapper(*args, **kwargs):
                return func(*args, **kwargs) + "!"

            return _wrapper

        
        
        
        
        
        
        decorators = (add_exclamation_mark, add_question_mark)

        @method_decorator(decorators, name="method")
        class TestFirst:
            def method(self):
                return "hello world"

        class TestSecond:
            @method_decorator(decorators)
            def method(self):
                return "hello world"

        self.assertEqual(TestFirst().method(), "hello world?!")
        self.assertEqual(TestSecond().method(), "hello world?!")

    def test_invalid_non_callable_attribute_decoration(self):
        
        msg = (
            "Cannot decorate 'prop' as it isn't a callable attribute of "
            "<class 'Test'> (1)"
        )
        with self.assertRaisesMessage(TypeError, msg):

            @method_decorator(lambda: None, name="prop")
            class Test:
                prop = 1

                @classmethod
                def __module__(cls):
                    return "tests"

    def test_invalid_method_name_to_decorate(self):
        
        msg = (
            "The keyword argument `name` must be the name of a method of the "
            "decorated class: <class 'Test'>. Got 'nonexistent_method' instead"
        )
        with self.assertRaisesMessage(ValueError, msg):

            @method_decorator(lambda: None, name="nonexistent_method")
            class Test:
                @classmethod
                def __module__(cls):
                    return "tests"

    def test_wrapper_assignments(self):
        
        func_name = None
        func_module = None

        def decorator(func):
            @wraps(func)
            def inner(*args, **kwargs):
                nonlocal func_name, func_module
                func_name = getattr(func, "__name__", None)
                func_module = getattr(func, "__module__", None)
                return func(*args, **kwargs)

            return inner

        class Test:
            @method_decorator(decorator)
            def method(self):
                return "tests"

        Test().method()
        self.assertEqual(func_name, "method")
        self.assertIsNotNone(func_module)