Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/template/context.py: 27%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

176 statements  

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 isinstance(value, BaseContext): 

35 self.dicts += value.dicts[1:] 

36 elif value is not None: 

37 self.dicts.append(value) 

38 

39 def __copy__(self): 

40 duplicate = BaseContext() 

41 duplicate.__class__ = self.__class__ 

42 duplicate.__dict__ = copy(self.__dict__) 

43 duplicate.dicts = self.dicts[:] 

44 return duplicate 

45 

46 def __repr__(self): 

47 return repr(self.dicts) 

48 

49 def __iter__(self): 

50 return reversed(self.dicts) 

51 

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

53 dicts = [] 

54 for d in args: 

55 if isinstance(d, BaseContext): 

56 dicts += d.dicts[1:] 

57 else: 

58 dicts.append(d) 

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

60 

61 def pop(self): 

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

63 raise ContextPopException 

64 return self.dicts.pop() 

65 

66 def __setitem__(self, key, value): 

67 "Set a variable in the current context" 

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

69 

70 def set_upward(self, key, value): 

71 """ 

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

73 otherwise in the current context. 

74 """ 

75 context = self.dicts[-1] 

76 for d in reversed(self.dicts): 

77 if key in d: 

78 context = d 

79 break 

80 context[key] = value 

81 

82 def __getitem__(self, key): 

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

84 for d in reversed(self.dicts): 

85 if key in d: 

86 return d[key] 

87 raise KeyError(key) 

88 

89 def __delitem__(self, key): 

90 "Delete a variable from the current context" 

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

92 

93 def __contains__(self, key): 

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

95 

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

97 for d in reversed(self.dicts): 

98 if key in d: 

99 return d[key] 

100 return otherwise 

101 

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

103 try: 

104 return self[key] 

105 except KeyError: 

106 self[key] = default 

107 return default 

108 

109 def new(self, values=None): 

110 """ 

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

112 values given in 'values' stored. 

113 """ 

114 new_context = copy(self) 

115 new_context._reset_dicts(values) 

116 return new_context 

117 

118 def flatten(self): 

119 """ 

120 Return self.dicts as one dictionary. 

121 """ 

122 flat = {} 

123 for d in self.dicts: 

124 flat.update(d) 

125 return flat 

126 

127 def __eq__(self, other): 

128 """ 

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

130 """ 

131 if not isinstance(other, BaseContext): 

132 return NotImplemented 

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

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

135 

136 

137class Context(BaseContext): 

138 "A stack container for variable context" 

139 

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

141 self.autoescape = autoescape 

142 self.use_l10n = use_l10n 

143 self.use_tz = use_tz 

144 self.template_name = "unknown" 

145 self.render_context = RenderContext() 

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

147 # templates -- during rendering, see bind_template. 

148 self.template = None 

149 super().__init__(dict_) 

150 

151 @contextmanager 

152 def bind_template(self, template): 

153 if self.template is not None: 

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

155 self.template = template 

156 try: 

157 yield 

158 finally: 

159 self.template = None 

160 

161 def __copy__(self): 

162 duplicate = super().__copy__() 

163 duplicate.render_context = copy(self.render_context) 

164 return duplicate 

165 

166 def update(self, other_dict): 

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

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

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

170 if isinstance(other_dict, BaseContext): 

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

172 return ContextDict(self, other_dict) 

173 

174 

175class RenderContext(BaseContext): 

176 """ 

177 A stack container for storing Template state. 

178 

179 RenderContext simplifies the implementation of template Nodes by providing a 

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

181 

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

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

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

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

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

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

188 template context. 

189 """ 

190 

191 template = None 

192 

193 def __iter__(self): 

194 yield from self.dicts[-1] 

195 

196 def __contains__(self, key): 

197 return key in self.dicts[-1] 

198 

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

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

201 

202 def __getitem__(self, key): 

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

204 

205 @contextmanager 

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

207 initial = self.template 

208 self.template = template 

209 if isolated_context: 

210 self.push() 

211 try: 

212 yield 

213 finally: 

214 self.template = initial 

215 if isolated_context: 

216 self.pop() 

217 

218 

219class RequestContext(Context): 

220 """ 

221 This subclass of template.Context automatically populates itself using 

222 the processors defined in the engine's configuration. 

223 Additional processors can be specified as a list of callables 

224 using the "processors" keyword argument. 

225 """ 

226 

227 def __init__( 

228 self, 

229 request, 

230 dict_=None, 

231 processors=None, 

232 use_l10n=None, 

233 use_tz=None, 

234 autoescape=True, 

235 ): 

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

237 self.request = request 

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

239 self._processors_index = len(self.dicts) 

240 

241 # placeholder for context processors output 

242 self.update({}) 

243 

244 # empty dict for any new modifications 

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

246 self.update({}) 

247 

248 @contextmanager 

249 def bind_template(self, template): 

250 if self.template is not None: 

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

252 

253 self.template = template 

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

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

256 updates = {} 

257 for processor in processors: 

258 context = processor(self.request) 

259 try: 

260 updates.update(context) 

261 except TypeError as e: 

262 raise TypeError( 

263 f"Context processor {processor.__qualname__} didn't return a " 

264 "dictionary." 

265 ) from e 

266 

267 self.dicts[self._processors_index] = updates 

268 

269 try: 

270 yield 

271 finally: 

272 self.template = None 

273 # Unset context processors. 

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

275 

276 def new(self, values=None): 

277 new_context = super().new(values) 

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

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

280 if hasattr(new_context, "_processors_index"): 

281 del new_context._processors_index 

282 return new_context 

283 

284 

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

286 """ 

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

288 """ 

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

290 raise TypeError( 

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

292 ) 

293 if request is None: 

294 context = Context(context, **kwargs) 

295 else: 

296 # The following pattern is required to ensure values from 

297 # context override those from template context processors. 

298 original_context = context 

299 context = RequestContext(request, **kwargs) 

300 if original_context: 

301 context.push(original_context) 

302 return context