Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/template/context.py: 26%

168 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from contextlib import contextmanager 

2from copy import copy 

3 

4# Hard-coded processor for easier use of CSRF protection. 

5_builtin_context_processors = ("django.template.context_processors.csrf",) 

6 

7 

8class ContextPopException(Exception): 

9 "pop() has been called more times than push()" 

10 pass 

11 

12 

13class ContextDict(dict): 

14 def __init__(self, context, *args, **kwargs): 

15 super().__init__(*args, **kwargs) 

16 

17 context.dicts.append(self) 

18 self.context = context 

19 

20 def __enter__(self): 

21 return self 

22 

23 def __exit__(self, *args, **kwargs): 

24 self.context.pop() 

25 

26 

27class BaseContext: 

28 def __init__(self, dict_=None): 

29 self._reset_dicts(dict_) 

30 

31 def _reset_dicts(self, value=None): 

32 builtins = {"True": True, "False": False, "None": None} 

33 self.dicts = [builtins] 

34 if value is not None: 

35 self.dicts.append(value) 

36 

37 def __copy__(self): 

38 duplicate = copy(super()) 

39 duplicate.dicts = self.dicts[:] 

40 return duplicate 

41 

42 def __repr__(self): 

43 return repr(self.dicts) 

44 

45 def __iter__(self): 

46 return reversed(self.dicts) 

47 

48 def push(self, *args, **kwargs): 

49 dicts = [] 

50 for d in args: 

51 if isinstance(d, BaseContext): 

52 dicts += d.dicts[1:] 

53 else: 

54 dicts.append(d) 

55 return ContextDict(self, *dicts, **kwargs) 

56 

57 def pop(self): 

58 if len(self.dicts) == 1: 

59 raise ContextPopException 

60 return self.dicts.pop() 

61 

62 def __setitem__(self, key, value): 

63 "Set a variable in the current context" 

64 self.dicts[-1][key] = value 

65 

66 def set_upward(self, key, value): 

67 """ 

68 Set a variable in one of the higher contexts if it exists there, 

69 otherwise in the current context. 

70 """ 

71 context = self.dicts[-1] 

72 for d in reversed(self.dicts): 

73 if key in d: 

74 context = d 

75 break 

76 context[key] = value 

77 

78 def __getitem__(self, key): 

79 "Get a variable's value, starting at the current context and going upward" 

80 for d in reversed(self.dicts): 

81 if key in d: 

82 return d[key] 

83 raise KeyError(key) 

84 

85 def __delitem__(self, key): 

86 "Delete a variable from the current context" 

87 del self.dicts[-1][key] 

88 

89 def __contains__(self, key): 

90 return any(key in d for d in self.dicts) 

91 

92 def get(self, key, otherwise=None): 

93 for d in reversed(self.dicts): 

94 if key in d: 

95 return d[key] 

96 return otherwise 

97 

98 def setdefault(self, key, default=None): 

99 try: 

100 return self[key] 

101 except KeyError: 

102 self[key] = default 

103 return default 

104 

105 def new(self, values=None): 

106 """ 

107 Return a new context with the same properties, but with only the 

108 values given in 'values' stored. 

109 """ 

110 new_context = copy(self) 

111 new_context._reset_dicts(values) 

112 return new_context 

113 

114 def flatten(self): 

115 """ 

116 Return self.dicts as one dictionary. 

117 """ 

118 flat = {} 

119 for d in self.dicts: 

120 flat.update(d) 

121 return flat 

122 

123 def __eq__(self, other): 

124 """ 

125 Compare two contexts by comparing theirs 'dicts' attributes. 

126 """ 

127 if not isinstance(other, BaseContext): 

128 return NotImplemented 

129 # flatten dictionaries because they can be put in a different order. 

130 return self.flatten() == other.flatten() 

131 

132 

133class Context(BaseContext): 

134 "A stack container for variable context" 

135 

136 def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None): 

137 self.autoescape = autoescape 

138 self.use_l10n = use_l10n 

139 self.use_tz = use_tz 

140 self.template_name = "unknown" 

141 self.render_context = RenderContext() 

142 # Set to the original template -- as opposed to extended or included 

143 # templates -- during rendering, see bind_template. 

144 self.template = None 

145 super().__init__(dict_) 

146 

147 @contextmanager 

148 def bind_template(self, template): 

149 if self.template is not None: 

150 raise RuntimeError("Context is already bound to a template") 

151 self.template = template 

152 try: 

153 yield 

154 finally: 

155 self.template = None 

156 

157 def __copy__(self): 

158 duplicate = super().__copy__() 

159 duplicate.render_context = copy(self.render_context) 

160 return duplicate 

161 

162 def update(self, other_dict): 

163 "Push other_dict to the stack of dictionaries in the Context" 

164 if not hasattr(other_dict, "__getitem__"): 

165 raise TypeError("other_dict must be a mapping (dictionary-like) object.") 

166 if isinstance(other_dict, BaseContext): 

167 other_dict = other_dict.dicts[1:].pop() 

168 return ContextDict(self, other_dict) 

169 

170 

171class RenderContext(BaseContext): 

172 """ 

173 A stack container for storing Template state. 

174 

175 RenderContext simplifies the implementation of template Nodes by providing a 

176 safe place to store state between invocations of a node's `render` method. 

177 

178 The RenderContext also provides scoping rules that are more sensible for 

179 'template local' variables. The render context stack is pushed before each 

180 template is rendered, creating a fresh scope with nothing in it. Name 

181 resolution fails if a variable is not found at the top of the RequestContext 

182 stack. Thus, variables are local to a specific template and don't affect the 

183 rendering of other templates as they would if they were stored in the normal 

184 template context. 

185 """ 

186 

187 template = None 

188 

189 def __iter__(self): 

190 yield from self.dicts[-1] 

191 

192 def __contains__(self, key): 

193 return key in self.dicts[-1] 

194 

195 def get(self, key, otherwise=None): 

196 return self.dicts[-1].get(key, otherwise) 

197 

198 def __getitem__(self, key): 

199 return self.dicts[-1][key] 

200 

201 @contextmanager 

202 def push_state(self, template, isolated_context=True): 

203 initial = self.template 

204 self.template = template 

205 if isolated_context: 

206 self.push() 

207 try: 

208 yield 

209 finally: 

210 self.template = initial 

211 if isolated_context: 

212 self.pop() 

213 

214 

215class RequestContext(Context): 

216 """ 

217 This subclass of template.Context automatically populates itself using 

218 the processors defined in the engine's configuration. 

219 Additional processors can be specified as a list of callables 

220 using the "processors" keyword argument. 

221 """ 

222 

223 def __init__( 

224 self, 

225 request, 

226 dict_=None, 

227 processors=None, 

228 use_l10n=None, 

229 use_tz=None, 

230 autoescape=True, 

231 ): 

232 super().__init__(dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape) 

233 self.request = request 

234 self._processors = () if processors is None else tuple(processors) 

235 self._processors_index = len(self.dicts) 

236 

237 # placeholder for context processors output 

238 self.update({}) 

239 

240 # empty dict for any new modifications 

241 # (so that context processors don't overwrite them) 

242 self.update({}) 

243 

244 @contextmanager 

245 def bind_template(self, template): 

246 if self.template is not None: 

247 raise RuntimeError("Context is already bound to a template") 

248 

249 self.template = template 

250 # Set context processors according to the template engine's settings. 

251 processors = template.engine.template_context_processors + self._processors 

252 updates = {} 

253 for processor in processors: 

254 updates.update(processor(self.request)) 

255 self.dicts[self._processors_index] = updates 

256 

257 try: 

258 yield 

259 finally: 

260 self.template = None 

261 # Unset context processors. 

262 self.dicts[self._processors_index] = {} 

263 

264 def new(self, values=None): 

265 new_context = super().new(values) 

266 # This is for backwards-compatibility: RequestContexts created via 

267 # Context.new don't include values from context processors. 

268 if hasattr(new_context, "_processors_index"): 

269 del new_context._processors_index 

270 return new_context 

271 

272 

273def make_context(context, request=None, **kwargs): 

274 """ 

275 Create a suitable Context from a plain dict and optionally an HttpRequest. 

276 """ 

277 if context is not None and not isinstance(context, dict): 

278 raise TypeError( 

279 "context must be a dict rather than %s." % context.__class__.__name__ 

280 ) 

281 if request is None: 

282 context = Context(context, **kwargs) 

283 else: 

284 # The following pattern is required to ensure values from 

285 # context override those from template context processors. 

286 original_context = context 

287 context = RequestContext(request, **kwargs) 

288 if original_context: 

289 context.push(original_context) 

290 return context