123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- "Functions that help with dynamically creating decorators for views."
- from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
- from django.utils import six
- class classonlymethod(classmethod):
- def __get__(self, instance, owner):
- if instance is not None:
- raise AttributeError("This method is available only on the view class.")
- return super(classonlymethod, self).__get__(instance, owner)
- 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.__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.
- 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, assigned=available_attrs(decorator))
- # Change the name to aid debugging.
- if hasattr(decorator, '__name__'):
- _dec.__name__ = 'method_decorator(%s)' % decorator.__name__
- else:
- _dec.__name__ = 'method_decorator(%s)' % decorator.__class__.__name__
- return _dec
- def decorator_from_middleware_with_args(middleware_class):
- """
- Like decorator_from_middleware, but returns 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), returns 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)()
- def available_attrs(fn):
- """
- Return the list of functools-wrappable attributes on a callable.
- This is required as a workaround for http://bugs.python.org/issue3445
- under Python 2.
- """
- if six.PY3:
- return WRAPPER_ASSIGNMENTS
- else:
- return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
- 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, assigned=available_attrs(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'):
- callback = lambda response: 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
|