Fixed #13093 -- Updated some decorators and the decorator_from_middleware function to allow callable classes to be decorated. Thanks to Brian Neal for the report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12762 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5c256ddeec
commit
615eab6b02
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import types
|
import types
|
||||||
try:
|
try:
|
||||||
from functools import wraps, update_wrapper
|
from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.utils.functional import wraps, update_wrapper # Python 2.3, 2.4 fallback.
|
from django.utils.functional import wraps, update_wrapper, WRAPPER_ASSIGNMENTS # Python 2.3, 2.4 fallback.
|
||||||
|
|
||||||
|
|
||||||
def method_decorator(decorator):
|
def method_decorator(decorator):
|
||||||
|
@ -50,6 +50,12 @@ def decorator_from_middleware(middleware_class):
|
||||||
"""
|
"""
|
||||||
return make_middleware_decorator(middleware_class)()
|
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.
|
||||||
|
"""
|
||||||
|
return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
|
||||||
|
|
||||||
def make_middleware_decorator(middleware_class):
|
def make_middleware_decorator(middleware_class):
|
||||||
def _make_decorator(*m_args, **m_kwargs):
|
def _make_decorator(*m_args, **m_kwargs):
|
||||||
|
@ -77,6 +83,6 @@ def make_middleware_decorator(middleware_class):
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
return response
|
return response
|
||||||
return wraps(view_func)(_wrapped_view)
|
return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
|
||||||
return _decorator
|
return _decorator
|
||||||
return _make_decorator
|
return _make_decorator
|
||||||
|
|
|
@ -16,7 +16,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
|
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
|
||||||
|
|
||||||
from django.utils.decorators import decorator_from_middleware_with_args
|
from django.utils.decorators import decorator_from_middleware_with_args, available_attrs
|
||||||
from django.utils.cache import patch_cache_control, add_never_cache_headers
|
from django.utils.cache import patch_cache_control, add_never_cache_headers
|
||||||
from django.middleware.cache import CacheMiddleware
|
from django.middleware.cache import CacheMiddleware
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ def cache_control(**kwargs):
|
||||||
response = viewfunc(request, *args, **kw)
|
response = viewfunc(request, *args, **kw)
|
||||||
patch_cache_control(response, **kwargs)
|
patch_cache_control(response, **kwargs)
|
||||||
return response
|
return response
|
||||||
return wraps(viewfunc)(_cache_controlled)
|
return wraps(viewfunc, assigned=available_attrs(viewfunc))(_cache_controlled)
|
||||||
return _cache_controller
|
return _cache_controller
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,4 +69,4 @@ def never_cache(view_func):
|
||||||
response = view_func(request, *args, **kwargs)
|
response = view_func(request, *args, **kwargs)
|
||||||
add_never_cache_headers(response)
|
add_never_cache_headers(response)
|
||||||
return response
|
return response
|
||||||
return wraps(view_func)(_wrapped_view_func)
|
return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view_func)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.middleware.csrf import CsrfViewMiddleware
|
from django.middleware.csrf import CsrfViewMiddleware
|
||||||
from django.utils.decorators import decorator_from_middleware
|
from django.utils.decorators import decorator_from_middleware, available_attrs
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -22,7 +23,7 @@ def csrf_response_exempt(view_func):
|
||||||
resp = view_func(*args, **kwargs)
|
resp = view_func(*args, **kwargs)
|
||||||
resp.csrf_exempt = True
|
resp.csrf_exempt = True
|
||||||
return resp
|
return resp
|
||||||
return wraps(view_func)(wrapped_view)
|
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
|
||||||
|
|
||||||
def csrf_view_exempt(view_func):
|
def csrf_view_exempt(view_func):
|
||||||
"""
|
"""
|
||||||
|
@ -34,7 +35,7 @@ def csrf_view_exempt(view_func):
|
||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
return view_func(*args, **kwargs)
|
return view_func(*args, **kwargs)
|
||||||
wrapped_view.csrf_exempt = True
|
wrapped_view.csrf_exempt = True
|
||||||
return wraps(view_func)(wrapped_view)
|
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
|
||||||
|
|
||||||
def csrf_exempt(view_func):
|
def csrf_exempt(view_func):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,7 +11,7 @@ from calendar import timegm
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from email.Utils import formatdate
|
from email.Utils import formatdate
|
||||||
|
|
||||||
from django.utils.decorators import decorator_from_middleware
|
from django.utils.decorators import decorator_from_middleware, available_attrs
|
||||||
from django.utils.http import parse_etags, quote_etag
|
from django.utils.http import parse_etags, quote_etag
|
||||||
from django.middleware.http import ConditionalGetMiddleware
|
from django.middleware.http import ConditionalGetMiddleware
|
||||||
from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse
|
from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse
|
||||||
|
@ -35,7 +35,7 @@ def require_http_methods(request_method_list):
|
||||||
if request.method not in request_method_list:
|
if request.method not in request_method_list:
|
||||||
return HttpResponseNotAllowed(request_method_list)
|
return HttpResponseNotAllowed(request_method_list)
|
||||||
return func(request, *args, **kwargs)
|
return func(request, *args, **kwargs)
|
||||||
return wraps(func)(inner)
|
return wraps(func, assigned=available_attrs(func))(inner)
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
require_GET = require_http_methods(["GET"])
|
require_GET = require_http_methods(["GET"])
|
||||||
|
|
|
@ -4,6 +4,7 @@ except ImportError:
|
||||||
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
|
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
|
||||||
|
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
|
from django.utils.decorators import available_attrs
|
||||||
|
|
||||||
def vary_on_headers(*headers):
|
def vary_on_headers(*headers):
|
||||||
"""
|
"""
|
||||||
|
@ -21,7 +22,7 @@ def vary_on_headers(*headers):
|
||||||
response = func(*args, **kwargs)
|
response = func(*args, **kwargs)
|
||||||
patch_vary_headers(response, headers)
|
patch_vary_headers(response, headers)
|
||||||
return response
|
return response
|
||||||
return wraps(func)(inner_func)
|
return wraps(func, assigned=available_attrs(func))(inner_func)
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def vary_on_cookie(func):
|
def vary_on_cookie(func):
|
||||||
|
@ -37,4 +38,4 @@ def vary_on_cookie(func):
|
||||||
response = func(*args, **kwargs)
|
response = func(*args, **kwargs)
|
||||||
patch_vary_headers(response, ('Cookie',))
|
patch_vary_headers(response, ('Cookie',))
|
||||||
return response
|
return response
|
||||||
return wraps(func)(inner_func)
|
return wraps(func, assigned=available_attrs(func))(inner_func)
|
||||||
|
|
|
@ -11,3 +11,9 @@ class DecoratorFromMiddlewareTests(TestCase):
|
||||||
Test a middleware that implements process_view.
|
Test a middleware that implements process_view.
|
||||||
"""
|
"""
|
||||||
self.client.get('/utils/xview/')
|
self.client.get('/utils/xview/')
|
||||||
|
|
||||||
|
def test_callable_process_view_middleware(self):
|
||||||
|
"""
|
||||||
|
Test a middleware that implements process_view, operating on a callable class.
|
||||||
|
"""
|
||||||
|
self.client.get('/utils/class_xview/')
|
||||||
|
|
|
@ -4,4 +4,5 @@ import views
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
(r'^xview/$', views.xview),
|
(r'^xview/$', views.xview),
|
||||||
|
(r'^class_xview/$', views.class_xview),
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,3 +8,10 @@ xview_dec = decorator_from_middleware(XViewMiddleware)
|
||||||
def xview(request):
|
def xview(request):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
xview = xview_dec(xview)
|
xview = xview_dec(xview)
|
||||||
|
|
||||||
|
|
||||||
|
class ClassXView(object):
|
||||||
|
def __call__(self, request):
|
||||||
|
return HttpResponse()
|
||||||
|
|
||||||
|
class_xview = xview_dec(ClassXView())
|
||||||
|
|
Loading…
Reference in New Issue