Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/utils/decorators.py: 38%
92 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"Functions that help with dynamically creating decorators for views."
3from functools import partial, update_wrapper, wraps
6class classonlymethod(classmethod):
7 def __get__(self, instance, cls=None):
8 if instance is not None: 8 ↛ 9line 8 didn't jump to line 9, because the condition on line 8 was never true
9 raise AttributeError(
10 "This method is available only on the class, not on instances."
11 )
12 return super().__get__(instance, cls)
15def _update_method_wrapper(_wrapper, decorator):
16 # _multi_decorate()'s bound_method isn't available in this scope. Cheat by
17 # using it on a dummy function.
18 @decorator
19 def dummy(*args, **kwargs):
20 pass
22 update_wrapper(_wrapper, dummy)
25def _multi_decorate(decorators, method):
26 """
27 Decorate `method` with one or more function decorators. `decorators` can be
28 a single decorator or an iterable of decorators.
29 """
30 if hasattr(decorators, "__iter__"): 30 ↛ 34line 30 didn't jump to line 34, because the condition on line 30 was never true
31 # Apply a list/tuple of decorators if 'decorators' is one. Decorator
32 # functions are applied so that the call order is the same as the
33 # order in which they appear in the iterable.
34 decorators = decorators[::-1]
35 else:
36 decorators = [decorators]
38 def _wrapper(self, *args, **kwargs):
39 # bound_method has the signature that 'decorator' expects i.e. no
40 # 'self' argument, but it's a closure over self so it can call
41 # 'func'. Also, wrap method.__get__() in a function because new
42 # attributes can't be set on bound method objects, only on functions.
43 bound_method = wraps(method)(partial(method.__get__(self, type(self))))
44 for dec in decorators:
45 bound_method = dec(bound_method)
46 return bound_method(*args, **kwargs)
48 # Copy any attributes that a decorator adds to the function it decorates.
49 for dec in decorators:
50 _update_method_wrapper(_wrapper, dec)
51 # Preserve any existing attributes of 'method', including the name.
52 update_wrapper(_wrapper, method)
53 return _wrapper
56def method_decorator(decorator, name=""):
57 """
58 Convert a function decorator into a method decorator
59 """
60 # 'obj' can be a class or a function. If 'obj' is a function at the time it
61 # is passed to _dec, it will eventually be a method of the class it is
62 # defined on. If 'obj' is a class, the 'name' is required to be the name
63 # of the method that will be decorated.
64 def _dec(obj):
65 if not isinstance(obj, type): 65 ↛ 67line 65 didn't jump to line 67, because the condition on line 65 was never false
66 return _multi_decorate(decorator, obj)
67 if not (name and hasattr(obj, name)):
68 raise ValueError(
69 "The keyword argument `name` must be the name of a method "
70 "of the decorated class: %s. Got '%s' instead." % (obj, name)
71 )
72 method = getattr(obj, name)
73 if not callable(method):
74 raise TypeError(
75 "Cannot decorate '%s' as it isn't a callable attribute of "
76 "%s (%s)." % (name, obj, method)
77 )
78 _wrapper = _multi_decorate(decorator, method)
79 setattr(obj, name, _wrapper)
80 return obj
82 # Don't worry about making _dec look similar to a list/tuple as it's rather
83 # meaningless.
84 if not hasattr(decorator, "__iter__"): 84 ↛ 87line 84 didn't jump to line 87, because the condition on line 84 was never false
85 update_wrapper(_dec, decorator)
86 # Change the name to aid debugging.
87 obj = decorator if hasattr(decorator, "__name__") else decorator.__class__
88 _dec.__name__ = "method_decorator(%s)" % obj.__name__
89 return _dec
92def decorator_from_middleware_with_args(middleware_class):
93 """
94 Like decorator_from_middleware, but return a function
95 that accepts the arguments to be passed to the middleware_class.
96 Use like::
98 cache_page = decorator_from_middleware_with_args(CacheMiddleware)
99 # ...
101 @cache_page(3600)
102 def my_view(request):
103 # ...
104 """
105 return make_middleware_decorator(middleware_class)
108def decorator_from_middleware(middleware_class):
109 """
110 Given a middleware class (not an instance), return a view decorator. This
111 lets you use middleware functionality on a per-view basis. The middleware
112 is created with no params passed.
113 """
114 return make_middleware_decorator(middleware_class)()
117def make_middleware_decorator(middleware_class):
118 def _make_decorator(*m_args, **m_kwargs):
119 def _decorator(view_func):
120 middleware = middleware_class(view_func, *m_args, **m_kwargs)
122 @wraps(view_func)
123 def _wrapped_view(request, *args, **kwargs):
124 if hasattr(middleware, "process_request"):
125 result = middleware.process_request(request)
126 if result is not None:
127 return result
128 if hasattr(middleware, "process_view"):
129 result = middleware.process_view(request, view_func, args, kwargs)
130 if result is not None:
131 return result
132 try:
133 response = view_func(request, *args, **kwargs)
134 except Exception as e:
135 if hasattr(middleware, "process_exception"):
136 result = middleware.process_exception(request, e)
137 if result is not None:
138 return result
139 raise
140 if hasattr(response, "render") and callable(response.render):
141 if hasattr(middleware, "process_template_response"):
142 response = middleware.process_template_response(
143 request, response
144 )
145 # Defer running of process_response until after the template
146 # has been rendered:
147 if hasattr(middleware, "process_response"):
149 def callback(response):
150 return middleware.process_response(request, response)
152 response.add_post_render_callback(callback)
153 else:
154 if hasattr(middleware, "process_response"):
155 return middleware.process_response(request, response)
156 return response
158 return _wrapped_view
160 return _decorator
162 return _make_decorator
165def sync_and_async_middleware(func):
166 """
167 Mark a middleware factory as returning a hybrid middleware supporting both
168 types of request.
169 """
170 func.sync_capable = True
171 func.async_capable = True
172 return func
175def sync_only_middleware(func):
176 """
177 Mark a middleware factory as returning a sync middleware.
178 This is the default.
179 """
180 func.sync_capable = True
181 func.async_capable = False
182 return func
185def async_only_middleware(func):
186 """Mark a middleware factory as returning an async middleware."""
187 func.sync_capable = False
188 func.async_capable = True
189 return func