jinja2.optimizer

The optimizer tries to constant fold expressions and modify the AST in place so that it should be faster to evaluate.

Because the AST does not contain all the scoping information and the compiler has to find that out, we cannot do all the optimizations we want. For example, loop unrolling doesn't work because unrolled loops would have a different scope. The solution would be a second syntax tree that stored the scoping rules.

 1"""The optimizer tries to constant fold expressions and modify the AST
 2in place so that it should be faster to evaluate.
 3
 4Because the AST does not contain all the scoping information and the
 5compiler has to find that out, we cannot do all the optimizations we
 6want. For example, loop unrolling doesn't work because unrolled loops
 7would have a different scope. The solution would be a second syntax tree
 8that stored the scoping rules.
 9"""
10
11import typing as t
12
13from . import nodes
14from .visitor import NodeTransformer
15
16if t.TYPE_CHECKING:
17    from .environment import Environment
18
19
20def optimize(node: nodes.Node, environment: "Environment") -> nodes.Node:
21    """The context hint can be used to perform an static optimization
22    based on the context given."""
23    optimizer = Optimizer(environment)
24    return t.cast(nodes.Node, optimizer.visit(node))
25
26
27class Optimizer(NodeTransformer):
28    def __init__(self, environment: "t.Optional[Environment]") -> None:
29        self.environment = environment
30
31    def generic_visit(
32        self, node: nodes.Node, *args: t.Any, **kwargs: t.Any
33    ) -> nodes.Node:
34        node = super().generic_visit(node, *args, **kwargs)
35
36        # Do constant folding. Some other nodes besides Expr have
37        # as_const, but folding them causes errors later on.
38        if isinstance(node, nodes.Expr):
39            try:
40                return nodes.Const.from_untrusted(
41                    node.as_const(args[0] if args else None),
42                    lineno=node.lineno,
43                    environment=self.environment,
44                )
45            except nodes.Impossible:
46                pass
47
48        return node
def optimize( node: jinja2.nodes.Node, environment: jinja2.environment.Environment) -> jinja2.nodes.Node:
21def optimize(node: nodes.Node, environment: "Environment") -> nodes.Node:
22    """The context hint can be used to perform an static optimization
23    based on the context given."""
24    optimizer = Optimizer(environment)
25    return t.cast(nodes.Node, optimizer.visit(node))

The context hint can be used to perform an static optimization based on the context given.

class Optimizer(jinja2.visitor.NodeTransformer):
28class Optimizer(NodeTransformer):
29    def __init__(self, environment: "t.Optional[Environment]") -> None:
30        self.environment = environment
31
32    def generic_visit(
33        self, node: nodes.Node, *args: t.Any, **kwargs: t.Any
34    ) -> nodes.Node:
35        node = super().generic_visit(node, *args, **kwargs)
36
37        # Do constant folding. Some other nodes besides Expr have
38        # as_const, but folding them causes errors later on.
39        if isinstance(node, nodes.Expr):
40            try:
41                return nodes.Const.from_untrusted(
42                    node.as_const(args[0] if args else None),
43                    lineno=node.lineno,
44                    environment=self.environment,
45                )
46            except nodes.Impossible:
47                pass
48
49        return node

Walks the abstract syntax tree and allows modifications of nodes.

The NodeTransformer will walk the AST and use the return value of the visitor functions to replace or remove the old node. If the return value of the visitor function is None the node will be removed from the previous location otherwise it's replaced with the return value. The return value may be the original node in which case no replacement takes place.

Optimizer(environment: Optional[jinja2.environment.Environment])
29    def __init__(self, environment: "t.Optional[Environment]") -> None:
30        self.environment = environment
environment
def generic_visit( self, node: jinja2.nodes.Node, *args: Any, **kwargs: Any) -> jinja2.nodes.Node:
32    def generic_visit(
33        self, node: nodes.Node, *args: t.Any, **kwargs: t.Any
34    ) -> nodes.Node:
35        node = super().generic_visit(node, *args, **kwargs)
36
37        # Do constant folding. Some other nodes besides Expr have
38        # as_const, but folding them causes errors later on.
39        if isinstance(node, nodes.Expr):
40            try:
41                return nodes.Const.from_untrusted(
42                    node.as_const(args[0] if args else None),
43                    lineno=node.lineno,
44                    environment=self.environment,
45                )
46            except nodes.Impossible:
47                pass
48
49        return node

Called if no explicit visitor function exists for a node.