|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- "Functions that help with dynamically creating decorators for views."
-
- # For backwards compatibility in Django 2.0.
- from contextlib import ContextDecorator # noqa
- from functools import WRAPPER_ASSIGNMENTS, partial, update_wrapper, wraps
-
-
- 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):
- # _multi_decorate()'s bound_method isn't available in this scope. Cheat by
- # using it on a dummy function.
- @decorator
- def dummy(*args, **kwargs):
- pass
- update_wrapper(_wrapper, dummy)
-
-
- def _multi_decorate(decorators, method):
- """
- Decorate `method` with one or more function decorators. `decorators` can be
- a single decorator or an iterable of decorators.
- """
- if hasattr(decorators, '__iter__'):
- # 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.
- decorators = decorators[::-1]
- else:
- decorators = [decorators]
-
- def _wrapper(self, *args, **kwargs):
- # 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.
- bound_method = partial(method.__get__(self, type(self)))
- for dec in decorators:
- bound_method = dec(bound_method)
- return bound_method(*args, **kwargs)
-
- # Copy any attributes that a decorator adds to the function it decorates.
- for dec in decorators:
- _update_method_wrapper(_wrapper, dec)
- # Preserve any existing attributes of 'method', including the name.
- update_wrapper(_wrapper, method)
- return _wrapper
-
-
- def method_decorator(decorator, 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.
- 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
-
- # Don't worry about making _dec look similar to a list/tuple as it's rather
- # meaningless.
- if not hasattr(decorator, '__iter__'):
- update_wrapper(_dec, decorator)
- # Change the name to aid debugging.
- 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):
- """
- 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):
- # ...
- """
- return make_middleware_decorator(middleware_class)
-
-
- def decorator_from_middleware(middleware_class):
- """
- 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.
- """
- return make_middleware_decorator(middleware_class)()
-
-
- # Unused, for backwards compatibility in Django 2.0.
- def available_attrs(fn):
- """
- Return the list of functools-wrappable attributes on a callable.
- This was required as a workaround for https://bugs.python.org/issue3445
- under Python 2.
- """
- return WRAPPER_ASSIGNMENTS
-
-
- def make_middleware_decorator(middleware_class):
- def _make_decorator(*m_args, **m_kwargs):
- middleware = middleware_class(*m_args, **m_kwargs)
-
- def _decorator(view_func):
- @wraps(view_func)
- def _wrapped_view(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
- try:
- response = view_func(request, *args, **kwargs)
- except Exception as e:
- if hasattr(middleware, 'process_exception'):
- result = middleware.process_exception(request, e)
- if result is not None:
- return result
- raise
- if hasattr(response, 'render') and callable(response.render):
- if hasattr(middleware, 'process_template_response'):
- response = middleware.process_template_response(request, response)
- # Defer running of process_response until after the template
- # has been rendered:
- 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
- return _wrapped_view
- return _decorator
- return _make_decorator
-
-
- class classproperty:
- def __init__(self, method=None):
- self.fget = method
-
- def __get__(self, instance, cls=None):
- return self.fget(cls)
-
- def getter(self, method):
- self.fget = method
- return self
|