@@ -150,3 +150,85 @@ def do_include(parser, token):
150
150
extra_context = namemap ,
151
151
isolated_context = isolated_context ,
152
152
)
153
+
154
+ def visit_extends (self , node , frame ):
155
+ """Dupe of the jinja2.compiler.CodeGenerator visit_Extends
156
+ except for
157
+ self.writeline(
158
+ "parent_template.new_context(context.get_all(), True,"
159
+ f" {self.dump_local_context(frame)})"
160
+ )
161
+ which is how we pull in context from yaml files to extended templates
162
+ """
163
+ from jinja2 .compiler import CompilerExit
164
+
165
+ if not frame .toplevel :
166
+ self .fail ("cannot use extend from a non top-level scope" , node .lineno )
167
+ # if the number of extends statements in general is zero so
168
+ # far, we don't have to add a check if something extended
169
+ # the template before this one.
170
+ if self .extends_so_far > 0 :
171
+ # if we have a known extends we just add a template runtime
172
+ # error into the generated code. We could catch that at compile
173
+ # time too, but i welcome it not to confuse users by throwing the
174
+ # same error at different times just "because we can".
175
+ if not self .has_known_extends :
176
+ self .writeline ("if parent_template is not None:" )
177
+ self .indent ()
178
+ self .writeline ('raise TemplateRuntimeError("extended multiple times")' )
179
+
180
+ # if we have a known extends already we don't need that code here
181
+ # as we know that the template execution will end here.
182
+ if self .has_known_extends :
183
+ raise CompilerExit ()
184
+ else :
185
+ self .outdent ()
186
+ self .writeline ("parent_template = environment.get_template(" , node )
187
+ self .visit (node .template , frame )
188
+ self .write (f", { self .name !r} )" )
189
+ # addition to update the context with dpl context
190
+ # calls the template_new_context method below when
191
+ # invoked at runtime
192
+ self .writeline (
193
+ "parent_template.new_context(context.get_all(), True,"
194
+ f" { self .dump_local_context (frame )} )"
195
+ )
196
+ self .writeline ("for name, parent_block in parent_template.blocks.items():" )
197
+ self .indent ()
198
+ self .writeline ("context.blocks.setdefault(name, []).append(parent_block)" )
199
+ self .outdent ()
200
+
201
+ # if this extends statement was in the root level we can take
202
+ # advantage of that information and simplify the generated code
203
+ # in the top level from this point onwards
204
+ if frame .rootlevel :
205
+ self .has_known_extends = True
206
+
207
+ # and now we have one more
208
+ self .extends_so_far += 1
209
+
210
+
211
+ def template_new_context (
212
+ self ,
213
+ vars = None , # noqa A002
214
+ shared = False ,
215
+ locals = None , # noqa A002
216
+ ):
217
+ """Create a new :class:`Context` for this template. The vars
218
+ provided will be passed to the template. Per default the globals
219
+ are added to the context. If shared is set to `True` the data
220
+ is passed as is to the context without adding the globals.
221
+
222
+ `locals` can be a dict of local variables for internal usage.
223
+ """
224
+ from jinja2 .runtime import new_context
225
+
226
+ if is_pattern_library_context (vars or {}) and (
227
+ pattern_context := get_pattern_context (self .name )
228
+ ):
229
+ for k , v in pattern_context .items ():
230
+ vars .setdefault (k , v )
231
+
232
+ return new_context (
233
+ self .environment , self .name , self .blocks , vars , shared , self .globals , locals
234
+ )
0 commit comments