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

1"Functions that help with dynamically creating decorators for views." 

2 

3from functools import partial, update_wrapper, wraps 

4 

5 

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) 

13 

14 

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 

21 

22 update_wrapper(_wrapper, dummy) 

23 

24 

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] 

37 

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) 

47 

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 

54 

55 

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 

81 

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 

90 

91 

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:: 

97 

98 cache_page = decorator_from_middleware_with_args(CacheMiddleware) 

99 # ... 

100 

101 @cache_page(3600) 

102 def my_view(request): 

103 # ... 

104 """ 

105 return make_middleware_decorator(middleware_class) 

106 

107 

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)() 

115 

116 

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) 

121 

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"): 

148 

149 def callback(response): 

150 return middleware.process_response(request, response) 

151 

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 

157 

158 return _wrapped_view 

159 

160 return _decorator 

161 

162 return _make_decorator 

163 

164 

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 

173 

174 

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 

183 

184 

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