summaryrefslogtreecommitdiff
path: root/django/utils
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2009-09-21 22:31:51 +0000
committerLuke Plant <L.Plant.98@cantab.net>2009-09-21 22:31:51 +0000
commitafeafcd492eec9f9cb333c8c55502c1c50b3b151 (patch)
treee86ff8c27deec468e4220e3d9fbfcd8b2a80ebe7 /django/utils
parentd56c1ab7f07eeaae3a0fc996169be7cae6a94bf4 (diff)
Fixed #6371 - several decorators don't work with bound methods.
This involved changing the way the internal function decorator_from_middleware works slightly, breaking some code that relied on the old behaviour. As a result, it is much simpler, but cache_page has been made slightly more complex to cope with the change. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11586 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/utils')
-rw-r--r--django/utils/decorators.py124
1 files changed, 76 insertions, 48 deletions
diff --git a/django/utils/decorators.py b/django/utils/decorators.py
index 8fc4c1d96a..3d616f2627 100644
--- a/django/utils/decorators.py
+++ b/django/utils/decorators.py
@@ -2,60 +2,88 @@
import types
try:
- from functools import wraps
+ from functools import wraps, update_wrapper
except ImportError:
- from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
+ from django.utils.functional import wraps, update_wrapper # Python 2.3, 2.4 fallback.
-def decorator_from_middleware(middleware_class):
+class MethodDecoratorAdaptor(object):
"""
- Given a middleware class (not an instance), returns a view decorator. This
- lets you use middleware functionality on a per-view basis.
+ Generic way of creating decorators that adapt to being
+ used on methods
+ """
+ def __init__(self, decorator, func):
+ update_wrapper(self, func)
+ # NB: update the __dict__ first, *then* set
+ # our own .func and .decorator, in case 'func' is actually
+ # another MethodDecoratorAdaptor object, which has its
+ # 'func' and 'decorator' attributes in its own __dict__
+ self.decorator = decorator
+ self.func = func
+ def __call__(self, *args, **kwargs):
+ return self.decorator(self.func)(*args, **kwargs)
+ def __get__(self, instance, owner):
+ return self.decorator(self.func.__get__(instance, owner))
+ def _get_name(self):
+ return self.func.__name__
+ def _get_doc(self):
+ return self.func.__doc__
+
+def auto_adapt_to_methods(decorator):
+ """Allows you to use the same decorator on methods and functions,
+ hiding the self argument from the decorator."""
+ def adapt(func):
+ return MethodDecoratorAdaptor(decorator, func)
+ return wraps(decorator)(adapt)
+
+def decorator_from_middleware_with_args(middleware_class):
"""
- def _decorator_from_middleware(*args, **kwargs):
- # For historical reasons, these "decorators" are also called as
- # dec(func, *args) instead of dec(*args)(func). We handle both forms
- # for backwards compatibility.
- has_func = True
- try:
- view_func = kwargs.pop('view_func')
- except KeyError:
- if len(args):
- view_func, args = args[0], args[1:]
- else:
- has_func = False
- if not (has_func and isinstance(view_func, types.FunctionType)):
- # We are being called as a decorator.
- if has_func:
- args = (view_func,) + args
- middleware = middleware_class(*args, **kwargs)
+ Like decorator_from_middleware, but returns a function
+ that accepts the arguments to be passed to the middleware_class.
+ Use like::
- def decorator_func(fn):
- return _decorator_from_middleware(fn, *args, **kwargs)
- return decorator_func
+ cache_page = decorator_from_middleware(CacheMiddleware)
+ # ...
- middleware = middleware_class(*args, **kwargs)
+ @cache_page(3600)
+ def my_view(request):
+ # ...
+ """
+ return make_middleware_decorator(middleware_class)
- 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, e:
- if hasattr(middleware, 'process_exception'):
- result = middleware.process_exception(request, e)
+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 make_middleware_decorator(middleware_class):
+ def _make_decorator(*m_args, **m_kwargs):
+ middleware = middleware_class(*m_args, **m_kwargs)
+ def _decorator(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, e:
+ if hasattr(middleware, 'process_exception'):
+ result = middleware.process_exception(request, e)
+ if result is not None:
+ return result
+ raise
+ if hasattr(middleware, 'process_response'):
+ result = middleware.process_response(request, response)
if result is not None:
return result
- raise
- if hasattr(middleware, 'process_response'):
- result = middleware.process_response(request, response)
- if result is not None:
- return result
- return response
- return wraps(view_func)(_wrapped_view)
- return _decorator_from_middleware
+ return response
+ return wraps(view_func)(_wrapped_view)
+ return auto_adapt_to_methods(_decorator)
+ return _make_decorator