Skip to content

Commit

Permalink
Fixed #13093 -- Updated some decorators and the decorator_from_middle…
Browse files Browse the repository at this point in the history
…ware 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
  • Loading branch information
freakboy3742 committed Mar 12, 2010
1 parent 5c256dd commit 615eab6
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 13 deletions.
12 changes: 9 additions & 3 deletions django/utils/decorators.py
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -77,6 +83,6 @@ def _wrapped_view(request, *args, **kwargs):
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
6 changes: 3 additions & 3 deletions django/views/decorators/cache.py
Expand Up @@ -16,7 +16,7 @@
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


Expand Down Expand Up @@ -56,7 +56,7 @@ def _cache_controlled(request, *args, **kw):
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




Expand All @@ -69,4 +69,4 @@ def _wrapped_view_func(request, *args, **kwargs):
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)
7 changes: 4 additions & 3 deletions django/views/decorators/csrf.py
@@ -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:
Expand All @@ -22,7 +23,7 @@ def wrapped_view(*args, **kwargs):
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):
""" """
Expand All @@ -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):
""" """
Expand Down
4 changes: 2 additions & 2 deletions django/views/decorators/http.py
Expand Up @@ -11,7 +11,7 @@
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
Expand All @@ -35,7 +35,7 @@ def inner(request, *args, **kwargs):
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"])
Expand Down
5 changes: 3 additions & 2 deletions django/views/decorators/vary.py
Expand Up @@ -4,6 +4,7 @@
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):
""" """
Expand All @@ -21,7 +22,7 @@ def inner_func(*args, **kwargs):
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):
Expand All @@ -37,4 +38,4 @@ def inner_func(*args, **kwargs):
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)
6 changes: 6 additions & 0 deletions tests/regressiontests/utils/decorators.py
Expand Up @@ -11,3 +11,9 @@ def test_process_view_middleware(self):
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/')
1 change: 1 addition & 0 deletions tests/regressiontests/utils/urls.py
Expand Up @@ -4,4 +4,5 @@


urlpatterns = patterns('', urlpatterns = patterns('',
(r'^xview/$', views.xview), (r'^xview/$', views.xview),
(r'^class_xview/$', views.class_xview),
) )
7 changes: 7 additions & 0 deletions tests/regressiontests/utils/views.py
Expand Up @@ -8,3 +8,10 @@
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())

0 comments on commit 615eab6

Please sign in to comment.