@@ -259,6 +259,11 @@ class Environment:
259259 `enable_async`
260260 If set to true this enables async template execution which
261261 allows using async functions and generators.
262+
263+ `remember_parsed_names`
264+ Should we remember parsed names? This is useful for dynamic
265+ dependency tracking, see `extract_parsed_names` for details.
266+ Default is ``False``.
262267 """
263268
264269 #: if this environment is sandboxed. Modifying this variable won't make
@@ -313,6 +318,7 @@ def __init__(
313318 auto_reload : bool = True ,
314319 bytecode_cache : t .Optional ["BytecodeCache" ] = None ,
315320 enable_async : bool = False ,
321+ remember_parsed_names : bool = False ,
316322 ):
317323 # !!Important notice!!
318324 # The constructor accepts quite a few arguments that should be
@@ -365,6 +371,11 @@ def __init__(
365371 self .is_async = enable_async
366372 _environment_config_check (self )
367373
374+ # dependency tracking
375+ self .parsed_names : t .Optional [t .List [str ]] = (
376+ [] if remember_parsed_names else None
377+ )
378+
368379 def add_extension (self , extension : t .Union [str , t .Type ["Extension" ]]) -> None :
369380 """Adds an extension after the environment was created.
370381
@@ -614,8 +625,36 @@ def _parse(
614625 self , source : str , name : t .Optional [str ], filename : t .Optional [str ]
615626 ) -> nodes .Template :
616627 """Internal parsing function used by `parse` and `compile`."""
628+ if name is not None and self .parsed_names is not None :
629+ self .parsed_names .append (name )
617630 return Parser (self , source , name , filename ).parse ()
618631
632+ def extract_parsed_names (self ) -> t .Optional [t .List [str ]]:
633+ """Return all template names that have been parsed so far, and clear the list.
634+
635+ This is enabled if `remember_parsed_names = True` was passed to the
636+ `Environment` constructor, otherwise it returns `None`. It can be used
637+ after `Template.render()` to extract dependency information. Compared
638+ to `jinja2.meta.find_referenced_templates()`, it:
639+
640+ a. works on dynamic inheritance and includes
641+ b. does not work unless and until you actually render the template
642+
643+ Many buildsystems are unable to support (b), but some do e.g. [1], the
644+ key point being that if the target file does not exist, dependency
645+ information is not needed since the target file must be built anyway.
646+ In such cases, you may prefer this function due to (a).
647+
648+ [1] https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
649+
650+ .. versionadded:: 3.2
651+ """
652+ if self .parsed_names is None :
653+ return None
654+ names = self .parsed_names [:]
655+ self .parsed_names .clear ()
656+ return names
657+
619658 def lex (
620659 self ,
621660 source : str ,
0 commit comments