@@ -150,3 +150,85 @@ def do_include(parser, token):
150150 extra_context = namemap ,
151151 isolated_context = isolated_context ,
152152 )
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