Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.1.X] Fixed #13093 -- Updated some decorators and the decorator_fro…

…m_middleware function to allow callable classes to be decorated. Thanks to Brian Neal for the report.

Backport of r12762 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12763 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 9137c54353f3356e0f65e0218016e92c5bc9cc43 1 parent 705d6cc
Russell Keith-Magee authored March 12, 2010
13  django/utils/decorators.py
@@ -2,9 +2,16 @@
2 2
 
3 3
 import types
4 4
 try:
5  
-    from functools import wraps
  5
+    from functools import wraps, WRAPPER_ASSIGNMENTS
6 6
 except ImportError:
7  
-    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
  7
+    from django.utils.functional import wraps, WRAPPER_ASSIGNMENTS  # Python 2.3, 2.4 fallback.
  8
+
  9
+def available_attrs(fn):
  10
+    """
  11
+    Return the list of functools-wrappable attributes on a callable.
  12
+    This is required as a workaround for http://bugs.python.org/issue3445.
  13
+    """
  14
+    return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))
8 15
 
9 16
 def decorator_from_middleware(middleware_class):
10 17
     """
@@ -57,5 +64,5 @@ def _wrapped_view(request, *args, **kwargs):
57 64
                 if result is not None:
58 65
                     return result
59 66
             return response
60  
-        return wraps(view_func)(_wrapped_view)
  67
+        return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
61 68
     return _decorator_from_middleware
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
  19
+from django.utils.decorators import decorator_from_middleware, 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
 
@@ -31,7 +31,7 @@ def _cache_controlled(request, *args, **kw):
31 31
             patch_cache_control(response, **kwargs)
32 32
             return response
33 33
 
34  
-        return wraps(viewfunc)(_cache_controlled)
  34
+        return wraps(viewfunc, assigned=available_attrs(viewfunc))(_cache_controlled)
35 35
 
36 36
     return _cache_controller
37 37
 
@@ -44,4 +44,4 @@ def _wrapped_view_func(request, *args, **kwargs):
44 44
         response = view_func(request, *args, **kwargs)
45 45
         add_never_cache_headers(response)
46 46
         return response
47  
-    return wraps(view_func)(_wrapped_view_func)
  47
+    return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view_func)
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 9137c54

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