mirror of
https://github.com/django/django.git
synced 2025-12-19 07:12:25 +00:00
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
This commit is contained in:
parent
d56c1ab7f0
commit
afeafcd492
3 changed files with 155 additions and 53 deletions
|
|
@ -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.
|
||||
|
||||
class MethodDecoratorAdaptor(object):
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
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(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.
|
||||
lets you use middleware functionality on a per-view basis. The middleware
|
||||
is created with no params passed.
|
||||
"""
|
||||
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)
|
||||
return make_middleware_decorator(middleware_class)()
|
||||
|
||||
def decorator_func(fn):
|
||||
return _decorator_from_middleware(fn, *args, **kwargs)
|
||||
return decorator_func
|
||||
|
||||
middleware = middleware_class(*args, **kwargs)
|
||||
|
||||
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 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
|
||||
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
|
||||
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
|
||||
return response
|
||||
return wraps(view_func)(_wrapped_view)
|
||||
return auto_adapt_to_methods(_decorator)
|
||||
return _make_decorator
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue