multidecorate s boundmethod isn t available in this scope cheat by using it on a dummy function decorate method with one or more function decorators decorators can be a single decorator or an iterable of decorators apply a listtuple of decorators if decorators is one decorator functions are applied so that the call order is the same as the order in which they appear in the iterable boundmethod has the signature that decorator expects i e no self argument but it s a closure over self so it can call func also wrap method get in a function because new attributes can t be set on bound method objects only on functions copy any attributes that a decorator adds to the function it decorates preserve any existing attributes of method including the name convert a function decorator into a method decorator obj can be a class or a function if obj is a function at the time it is passed to dec it will eventually be a method of the class it is defined on if obj is a class the name is required to be the name of the method that will be decorated don t worry about making dec look similar to a listtuple as it s rather meaningless change the name to aid debugging like decoratorfrommiddleware but return a function that accepts the arguments to be passed to the middlewareclass use like cachepage decoratorfrommiddlewarewithargscachemiddleware cachepage3600 def myviewrequest given a middleware class not an instance return a view decorator this lets you use middleware functionality on a perview basis the middleware is created with no params passed defer running of processresponse until after the template has been rendered mark a middleware factory as returning a hybrid middleware supporting both types of request mark a middleware factory as returning a sync middleware this is the default mark a middleware factory as returning an async middleware func synccapable false func asynccapable true return func _multi_decorate s bound_method isn t available in this scope cheat by using it on a dummy function decorate method with one or more function decorators decorators can be a single decorator or an iterable of decorators apply a list tuple of decorators if decorators is one decorator functions are applied so that the call order is the same as the order in which they appear in the iterable bound_method has the signature that decorator expects i e no self argument but it s a closure over self so it can call func also wrap method __get__ in a function because new attributes can t be set on bound method objects only on functions copy any attributes that a decorator adds to the function it decorates preserve any existing attributes of method including the name convert a function decorator into a method decorator obj can be a class or a function if obj is a function at the time it is passed to _dec it will eventually be a method of the class it is defined on if obj is a class the name is required to be the name of the method that will be decorated don t worry about making _dec look similar to a list tuple as it s rather meaningless change the name to aid debugging like decorator_from_middleware but return a function that accepts the arguments to be passed to the middleware_class use like cache_page decorator_from_middleware_with_args cachemiddleware cache_page 3600 def my_view request given a middleware class not an instance return a view decorator this lets you use middleware functionality on a per view basis the middleware is created with no params passed defer running of process_response until after the template has been rendered mark a middleware factory as returning a hybrid middleware supporting both types of request mark a middleware factory as returning a sync middleware this is the default mark a middleware factory as returning an async middleware
"Functions that help with dynamically creating decorators for views."

from functools import partial, update_wrapper, wraps

from asgiref.sync import iscoroutinefunction


class classonlymethod(classmethod):
    def __get__(self, instance, cls=None):
        if instance is not None:
            raise AttributeError(
                "This method is available only on the class, not on instances."
            )
        return super().__get__(instance, cls)


def _update_method_wrapper(_wrapper, decorator):
    
    
    @decorator
    def dummy(*args, **kwargs):
        pass

    update_wrapper(_wrapper, dummy)


def _multi_decorate(decorators, method):
    
    if hasattr(decorators, "__iter__"):
        
        
        
        decorators = decorators[::-1]
    else:
        decorators = [decorators]

    def _wrapper(self, *args, **kwargs):
        
        
        
        
        bound_method = wraps(method)(partial(method.__get__(self, type(self))))
        for dec in decorators:
            bound_method = dec(bound_method)
        return bound_method(*args, **kwargs)

    
    for dec in decorators:
        _update_method_wrapper(_wrapper, dec)
    
    update_wrapper(_wrapper, method)
    return _wrapper


def method_decorator(decorator, name=""):
    

    
    
    
    
    def _dec(obj):
        if not isinstance(obj, type):
            return _multi_decorate(decorator, obj)
        if not (name and hasattr(obj, name)):
            raise ValueError(
                "The keyword argument `name` must be the name of a method "
                "of the decorated class: %s. Got '%s' instead." % (obj, name)
            )
        method = getattr(obj, name)
        if not callable(method):
            raise TypeError(
                "Cannot decorate '%s' as it isn't a callable attribute of "
                "%s (%s)." % (name, obj, method)
            )
        _wrapper = _multi_decorate(decorator, method)
        setattr(obj, name, _wrapper)
        return obj

    
    
    if not hasattr(decorator, "__iter__"):
        update_wrapper(_dec, decorator)
    
    obj = decorator if hasattr(decorator, "__name__") else decorator.__class__
    _dec.__name__ = "method_decorator(%s)" % obj.__name__
    return _dec


def decorator_from_middleware_with_args(middleware_class):
    
    return make_middleware_decorator(middleware_class)


def decorator_from_middleware(middleware_class):
    
    return make_middleware_decorator(middleware_class)()


def make_middleware_decorator(middleware_class):
    def _make_decorator(*m_args, **m_kwargs):
        def _decorator(view_func):
            middleware = middleware_class(view_func, *m_args, **m_kwargs)

            def _pre_process_request(request, *args, **kwargs):
                if hasattr(middleware, "process_request"):
                    result = middleware.process_request(request)
                    if result is not None:
                        return result
                if hasattr(middleware, "process_view"):
                    result = middleware.process_view(request, view_func, args, kwargs)
                    if result is not None:
                        return result
                return None

            def _process_exception(request, exception):
                if hasattr(middleware, "process_exception"):
                    result = middleware.process_exception(request, exception)
                    if result is not None:
                        return result
                raise

            def _post_process_request(request, response):
                if hasattr(response, "render") and callable(response.render):
                    if hasattr(middleware, "process_template_response"):
                        response = middleware.process_template_response(
                            request, response
                        )
                    
                    
                    if hasattr(middleware, "process_response"):

                        def callback(response):
                            return middleware.process_response(request, response)

                        response.add_post_render_callback(callback)
                else:
                    if hasattr(middleware, "process_response"):
                        return middleware.process_response(request, response)
                return response

            if iscoroutinefunction(view_func):

                async def _view_wrapper(request, *args, **kwargs):
                    result = _pre_process_request(request, *args, **kwargs)
                    if result is not None:
                        return result

                    try:
                        response = await view_func(request, *args, **kwargs)
                    except Exception as e:
                        result = _process_exception(request, e)
                        if result is not None:
                            return result

                    return _post_process_request(request, response)

            else:

                def _view_wrapper(request, *args, **kwargs):
                    result = _pre_process_request(request, *args, **kwargs)
                    if result is not None:
                        return result

                    try:
                        response = view_func(request, *args, **kwargs)
                    except Exception as e:
                        result = _process_exception(request, e)
                        if result is not None:
                            return result

                    return _post_process_request(request, response)

            return wraps(view_func)(_view_wrapper)

        return _decorator

    return _make_decorator


def sync_and_async_middleware(func):
    
    func.sync_capable = True
    func.async_capable = True
    return func


def sync_only_middleware(func):
    
    func.sync_capable = True
    func.async_capable = False
    return func


def async_only_middleware(func):
    
    func.sync_capable = False
    func.async_capable = True
    return func