jinja2.meta

Functions that expose information about templates that might be interesting for introspection.

  1"""Functions that expose information about templates that might be
  2interesting for introspection.
  3"""
  4
  5import typing as t
  6
  7from . import nodes
  8from .compiler import CodeGenerator
  9from .compiler import Frame
 10
 11if t.TYPE_CHECKING:
 12    from .environment import Environment
 13
 14
 15class TrackingCodeGenerator(CodeGenerator):
 16    """We abuse the code generator for introspection."""
 17
 18    def __init__(self, environment: "Environment") -> None:
 19        super().__init__(environment, "<introspection>", "<introspection>")
 20        self.undeclared_identifiers: t.Set[str] = set()
 21
 22    def write(self, x: str) -> None:
 23        """Don't write."""
 24
 25    def enter_frame(self, frame: Frame) -> None:
 26        """Remember all undeclared identifiers."""
 27        super().enter_frame(frame)
 28
 29        for _, (action, param) in frame.symbols.loads.items():
 30            if action == "resolve" and param not in self.environment.globals:
 31                self.undeclared_identifiers.add(param)
 32
 33
 34def find_undeclared_variables(ast: nodes.Template) -> t.Set[str]:
 35    """Returns a set of all variables in the AST that will be looked up from
 36    the context at runtime.  Because at compile time it's not known which
 37    variables will be used depending on the path the execution takes at
 38    runtime, all variables are returned.
 39
 40    >>> from jinja2 import Environment, meta
 41    >>> env = Environment()
 42    >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
 43    >>> meta.find_undeclared_variables(ast) == {'bar'}
 44    True
 45
 46    .. admonition:: Implementation
 47
 48       Internally the code generator is used for finding undeclared variables.
 49       This is good to know because the code generator might raise a
 50       :exc:`TemplateAssertionError` during compilation and as a matter of
 51       fact this function can currently raise that exception as well.
 52    """
 53    codegen = TrackingCodeGenerator(ast.environment)  # type: ignore
 54    codegen.visit(ast)
 55    return codegen.undeclared_identifiers
 56
 57
 58_ref_types = (nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include)
 59_RefType = t.Union[nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include]
 60
 61
 62def find_referenced_templates(ast: nodes.Template) -> t.Iterator[t.Optional[str]]:
 63    """Finds all the referenced templates from the AST.  This will return an
 64    iterator over all the hardcoded template extensions, inclusions and
 65    imports.  If dynamic inheritance or inclusion is used, `None` will be
 66    yielded.
 67
 68    >>> from jinja2 import Environment, meta
 69    >>> env = Environment()
 70    >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
 71    >>> list(meta.find_referenced_templates(ast))
 72    ['layout.html', None]
 73
 74    This function is useful for dependency tracking.  For example if you want
 75    to rebuild parts of the website after a layout template has changed.
 76    """
 77    template_name: t.Any
 78
 79    for node in ast.find_all(_ref_types):
 80        template: nodes.Expr = node.template  # type: ignore
 81
 82        if not isinstance(template, nodes.Const):
 83            # a tuple with some non consts in there
 84            if isinstance(template, (nodes.Tuple, nodes.List)):
 85                for template_name in template.items:
 86                    # something const, only yield the strings and ignore
 87                    # non-string consts that really just make no sense
 88                    if isinstance(template_name, nodes.Const):
 89                        if isinstance(template_name.value, str):
 90                            yield template_name.value
 91                    # something dynamic in there
 92                    else:
 93                        yield None
 94            # something dynamic we don't know about here
 95            else:
 96                yield None
 97            continue
 98        # constant is a basestring, direct template name
 99        if isinstance(template.value, str):
100            yield template.value
101        # a tuple or list (latter *should* not happen) made of consts,
102        # yield the consts that are strings.  We could warn here for
103        # non string values
104        elif isinstance(node, nodes.Include) and isinstance(
105            template.value, (tuple, list)
106        ):
107            for template_name in template.value:
108                if isinstance(template_name, str):
109                    yield template_name
110        # something else we don't care about, we could warn here
111        else:
112            yield None
class TrackingCodeGenerator(jinja2.compiler.CodeGenerator):
16class TrackingCodeGenerator(CodeGenerator):
17    """We abuse the code generator for introspection."""
18
19    def __init__(self, environment: "Environment") -> None:
20        super().__init__(environment, "<introspection>", "<introspection>")
21        self.undeclared_identifiers: t.Set[str] = set()
22
23    def write(self, x: str) -> None:
24        """Don't write."""
25
26    def enter_frame(self, frame: Frame) -> None:
27        """Remember all undeclared identifiers."""
28        super().enter_frame(frame)
29
30        for _, (action, param) in frame.symbols.loads.items():
31            if action == "resolve" and param not in self.environment.globals:
32                self.undeclared_identifiers.add(param)

We abuse the code generator for introspection.

TrackingCodeGenerator(environment: jinja2.environment.Environment)
19    def __init__(self, environment: "Environment") -> None:
20        super().__init__(environment, "<introspection>", "<introspection>")
21        self.undeclared_identifiers: t.Set[str] = set()
undeclared_identifiers: Set[str]
def write(self, x: str) -> None:
23    def write(self, x: str) -> None:
24        """Don't write."""

Don't write.

def enter_frame(self, frame: jinja2.compiler.Frame) -> None:
26    def enter_frame(self, frame: Frame) -> None:
27        """Remember all undeclared identifiers."""
28        super().enter_frame(frame)
29
30        for _, (action, param) in frame.symbols.loads.items():
31            if action == "resolve" and param not in self.environment.globals:
32                self.undeclared_identifiers.add(param)

Remember all undeclared identifiers.

Inherited Members
jinja2.compiler.CodeGenerator
environment
name
filename
stream
created_block_context
defer_init
optimizer
import_aliases
blocks
extends_so_far
has_known_extends
code_lineno
tests
filters
debug_info
optimized
fail
temporary_identifier
buffer
return_buffer_contents
indent
outdent
start_write
end_write
simple_write
blockvisit
writeline
newline
signature
pull_dependencies
leave_frame
choose_async
func
macro_body
macro_def
position
dump_local_context
write_commons
push_parameter_definitions
pop_parameter_definitions
mark_parameter_stored
push_context_reference
pop_context_reference
get_context_ref
get_resolve_func
derive_context
parameter_is_undeclared
push_assign_tracking
pop_assign_tracking
visit_Template
visit_Block
visit_Extends
visit_Include
visit_Import
visit_FromImport
visit_For
visit_If
visit_Macro
visit_CallBlock
visit_FilterBlock
visit_With
visit_ExprStmt
visit_Output
visit_Assign
visit_AssignBlock
visit_Name
visit_NSRef
visit_Const
visit_TemplateData
visit_Tuple
visit_List
visit_Dict
visit_Add
visit_Sub
visit_Mul
visit_Div
visit_FloorDiv
visit_Pow
visit_Mod
visit_And
visit_Or
visit_Pos
visit_Neg
visit_Not
visit_Concat
visit_Compare
visit_Operand
visit_Getattr
visit_Getitem
visit_Slice
visit_Filter
visit_Test
visit_CondExpr
visit_Call
visit_Keyword
visit_MarkSafe
visit_MarkSafeIfAutoescape
visit_EnvironmentAttribute
visit_ExtensionAttribute
visit_ImportedName
visit_InternalName
visit_ContextReference
visit_DerivedContextReference
visit_Continue
visit_Break
visit_Scope
visit_OverlayScope
visit_EvalContextModifier
visit_ScopedEvalContextModifier
jinja2.visitor.NodeVisitor
get_visitor
visit
generic_visit
def find_undeclared_variables(ast: jinja2.nodes.Template) -> Set[str]:
35def find_undeclared_variables(ast: nodes.Template) -> t.Set[str]:
36    """Returns a set of all variables in the AST that will be looked up from
37    the context at runtime.  Because at compile time it's not known which
38    variables will be used depending on the path the execution takes at
39    runtime, all variables are returned.
40
41    >>> from jinja2 import Environment, meta
42    >>> env = Environment()
43    >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
44    >>> meta.find_undeclared_variables(ast) == {'bar'}
45    True
46
47    .. admonition:: Implementation
48
49       Internally the code generator is used for finding undeclared variables.
50       This is good to know because the code generator might raise a
51       :exc:`TemplateAssertionError` during compilation and as a matter of
52       fact this function can currently raise that exception as well.
53    """
54    codegen = TrackingCodeGenerator(ast.environment)  # type: ignore
55    codegen.visit(ast)
56    return codegen.undeclared_identifiers

Returns a set of all variables in the AST that will be looked up from the context at runtime. Because at compile time it's not known which variables will be used depending on the path the execution takes at runtime, all variables are returned.

>>> from jinja2 import Environment, meta
>>> env = Environment()
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
>>> meta.find_undeclared_variables(ast) == {'bar'}
True

.. admonition:: Implementation

Internally the code generator is used for finding undeclared variables. This is good to know because the code generator might raise a TemplateAssertionError during compilation and as a matter of fact this function can currently raise that exception as well.

def find_referenced_templates(ast: jinja2.nodes.Template) -> Iterator[Optional[str]]:
 63def find_referenced_templates(ast: nodes.Template) -> t.Iterator[t.Optional[str]]:
 64    """Finds all the referenced templates from the AST.  This will return an
 65    iterator over all the hardcoded template extensions, inclusions and
 66    imports.  If dynamic inheritance or inclusion is used, `None` will be
 67    yielded.
 68
 69    >>> from jinja2 import Environment, meta
 70    >>> env = Environment()
 71    >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
 72    >>> list(meta.find_referenced_templates(ast))
 73    ['layout.html', None]
 74
 75    This function is useful for dependency tracking.  For example if you want
 76    to rebuild parts of the website after a layout template has changed.
 77    """
 78    template_name: t.Any
 79
 80    for node in ast.find_all(_ref_types):
 81        template: nodes.Expr = node.template  # type: ignore
 82
 83        if not isinstance(template, nodes.Const):
 84            # a tuple with some non consts in there
 85            if isinstance(template, (nodes.Tuple, nodes.List)):
 86                for template_name in template.items:
 87                    # something const, only yield the strings and ignore
 88                    # non-string consts that really just make no sense
 89                    if isinstance(template_name, nodes.Const):
 90                        if isinstance(template_name.value, str):
 91                            yield template_name.value
 92                    # something dynamic in there
 93                    else:
 94                        yield None
 95            # something dynamic we don't know about here
 96            else:
 97                yield None
 98            continue
 99        # constant is a basestring, direct template name
100        if isinstance(template.value, str):
101            yield template.value
102        # a tuple or list (latter *should* not happen) made of consts,
103        # yield the consts that are strings.  We could warn here for
104        # non string values
105        elif isinstance(node, nodes.Include) and isinstance(
106            template.value, (tuple, list)
107        ):
108            for template_name in template.value:
109                if isinstance(template_name, str):
110                    yield template_name
111        # something else we don't care about, we could warn here
112        else:
113            yield None

Finds all the referenced templates from the AST. This will return an iterator over all the hardcoded template extensions, inclusions and imports. If dynamic inheritance or inclusion is used, None will be yielded.

>>> from jinja2 import Environment, meta
>>> env = Environment()
>>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
>>> list(meta.find_referenced_templates(ast))
['layout.html', None]

This function is useful for dependency tracking. For example if you want to rebuild parts of the website after a layout template has changed.