Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #13093 -- Updated some decorators and the decorator_from_middle…

…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...
commit 615eab6b0255608e2094f0ecf02c5351a1e887cc 1 parent 5c256dd
Russell Keith-Magee authored March 12, 2010
12  django/utils/decorators.py
@@ -2,9 +2,9 @@
2 2
 
3 3
 import types
4 4
 try:
5  
-    from functools import wraps, update_wrapper
  5
+    from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS
6 6
 except ImportError:
7  
-    from django.utils.functional import wraps, update_wrapper  # Python 2.3, 2.4 fallback.
  7
+    from django.utils.functional import wraps, update_wrapper, WRAPPER_ASSIGNMENTS  # Python 2.3, 2.4 fallback.
8 8
 
9 9
 
10 10
 def method_decorator(decorator):
@@ -50,6 +50,12 @@ def decorator_from_middleware(middleware_class):
50 50
     """
51 51
     return make_middleware_decorator(middleware_class)()
52 52
 
  53
+def available_attrs(fn):
  54
+    """
  55
+    Return the list of functools-wrappable attributes on a callable.
  56
+    This is required as a workaround for http://bugs.python.org/issue3445.
  57
+    """
  58
+    return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
53 59
 
54 60
 def make_middleware_decorator(middleware_class):
55 61
     def _make_decorator(*m_args, **m_kwargs):
@@ -77,6 +83,6 @@ def _wrapped_view(request, *args, **kwargs):
77 83
                     if result is not None:
78 84
                         return result
79 85
                 return response
80  
-            return wraps(view_func)(_wrapped_view)
  86
+            return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
81 87
         return _decorator
82 88
     return _make_decorator
6  django/views/decorators/cache.py
@@ -16,7 +16,7 @@
16 16
 except ImportError:
17 17
     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
18 18
 
19  
-from django.utils.decorators import decorator_from_middleware_with_args
  19
+from django.utils.decorators import decorator_from_middleware_with_args, available_attrs
20 20
 from django.utils.cache import patch_cache_control, add_never_cache_headers
21 21
 from django.middleware.cache import CacheMiddleware
22 22
 
@@ -56,7 +56,7 @@ def _cache_controlled(request, *args, **kw):
56 56
             response = viewfunc(request, *args, **kw)
57 57
             patch_cache_control(response, **kwargs)
58 58
             return response
59  
-        return wraps(viewfunc)(_cache_controlled)
  59
+        return wraps(viewfunc, assigned=available_attrs(viewfunc))(_cache_controlled)
60 60
     return _cache_controller
61 61
 
62 62
 
@@ -69,4 +69,4 @@ def _wrapped_view_func(request, *args, **kwargs):
69 69
         response = view_func(request, *args, **kwargs)
70 70
         add_never_cache_headers(response)
71 71
         return response
72  
-    return wraps(view_func)(_wrapped_view_func)
  72
+    return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view_func)
7  django/views/decorators/csrf.py
... ...
@@ -1,5 +1,6 @@
1 1
 from django.middleware.csrf import CsrfViewMiddleware
2  
-from django.utils.decorators import decorator_from_middleware
  2
+from django.utils.decorators import decorator_from_middleware, available_attrs
  3
+
3 4
 try:
4 5
     from functools import wraps
5 6
 except ImportError:
@@ -22,7 +23,7 @@ def wrapped_view(*args, **kwargs):
22 23
         resp = view_func(*args, **kwargs)
23 24
         resp.csrf_exempt = True
24 25
         return resp
25  
-    return wraps(view_func)(wrapped_view)
  26
+    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
26 27
 
27 28
 def csrf_view_exempt(view_func):
28 29
     """
@@ -34,7 +35,7 @@ def csrf_view_exempt(view_func):
34 35
     def wrapped_view(*args, **kwargs):
35 36
         return view_func(*args, **kwargs)
36 37
     wrapped_view.csrf_exempt = True
37  
-    return wraps(view_func)(wrapped_view)
  38
+    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
38 39
 
39 40
 def csrf_exempt(view_func):
40 41
     """
4  django/views/decorators/http.py
@@ -11,7 +11,7 @@
11 11
 from datetime import timedelta
12 12
 from email.Utils import formatdate
13 13
 
14  
-from django.utils.decorators import decorator_from_middleware
  14
+from django.utils.decorators import decorator_from_middleware, available_attrs
15 15
 from django.utils.http import parse_etags, quote_etag
16 16
 from django.middleware.http import ConditionalGetMiddleware
17 17
 from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse
@@ -35,7 +35,7 @@ def inner(request, *args, **kwargs):
35 35
             if request.method not in request_method_list:
36 36
                 return HttpResponseNotAllowed(request_method_list)
37 37
             return func(request, *args, **kwargs)
38  
-        return wraps(func)(inner)
  38
+        return wraps(func, assigned=available_attrs(func))(inner)
39 39
     return decorator
40 40
 
41 41
 require_GET = require_http_methods(["GET"])
5  django/views/decorators/vary.py
@@ -4,6 +4,7 @@
4 4
     from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
5 5
 
6 6
 from django.utils.cache import patch_vary_headers
  7
+from django.utils.decorators import available_attrs
7 8
 
8 9
 def vary_on_headers(*headers):
9 10
     """
@@ -21,7 +22,7 @@ def inner_func(*args, **kwargs):
21 22
             response = func(*args, **kwargs)
22 23
             patch_vary_headers(response, headers)
23 24
             return response
24  
-        return wraps(func)(inner_func)
  25
+        return wraps(func, assigned=available_attrs(func))(inner_func)
25 26
     return decorator
26 27
 
27 28
 def vary_on_cookie(func):
@@ -37,4 +38,4 @@ def inner_func(*args, **kwargs):
37 38
         response = func(*args, **kwargs)
38 39
         patch_vary_headers(response, ('Cookie',))
39 40
         return response
40  
-    return wraps(func)(inner_func)
  41
+    return wraps(func, assigned=available_attrs(func))(inner_func)
6  tests/regressiontests/utils/decorators.py
@@ -11,3 +11,9 @@ def test_process_view_middleware(self):
11 11
         Test a middleware that implements process_view.
12 12
         """
13 13
         self.client.get('/utils/xview/')
  14
+
  15
+    def test_callable_process_view_middleware(self):
  16
+        """
  17
+        Test a middleware that implements process_view, operating on a callable class.
  18
+        """
  19
+        self.client.get('/utils/class_xview/')
1  tests/regressiontests/utils/urls.py
@@ -4,4 +4,5 @@
4 4
 
5 5
 urlpatterns = patterns('',
6 6
     (r'^xview/$', views.xview),
  7
+    (r'^class_xview/$', views.class_xview),
7 8
 )
7  tests/regressiontests/utils/views.py
@@ -8,3 +8,10 @@
8 8
 def xview(request):
9 9
     return HttpResponse()
10 10
 xview = xview_dec(xview)
  11
+
  12
+
  13
+class ClassXView(object):
  14
+    def __call__(self, request):
  15
+        return HttpResponse()
  16
+
  17
+class_xview = xview_dec(ClassXView())

0 notes on commit 615eab6

Please sign in to comment.
Something went wrong with that request. Please try again.