jinja2.compiler

Compiles nodes from the parser into Python code.

   1"""Compiles nodes from the parser into Python code."""
   2import typing as t
   3from contextlib import contextmanager
   4from functools import update_wrapper
   5from io import StringIO
   6from itertools import chain
   7from keyword import iskeyword as is_python_keyword
   8
   9from markupsafe import escape
  10from markupsafe import Markup
  11
  12from . import nodes
  13from .exceptions import TemplateAssertionError
  14from .idtracking import Symbols
  15from .idtracking import VAR_LOAD_ALIAS
  16from .idtracking import VAR_LOAD_PARAMETER
  17from .idtracking import VAR_LOAD_RESOLVE
  18from .idtracking import VAR_LOAD_UNDEFINED
  19from .nodes import EvalContext
  20from .optimizer import Optimizer
  21from .utils import _PassArg
  22from .utils import concat
  23from .visitor import NodeVisitor
  24
  25if t.TYPE_CHECKING:
  26    import typing_extensions as te
  27    from .environment import Environment
  28
  29F = t.TypeVar("F", bound=t.Callable[..., t.Any])
  30
  31operators = {
  32    "eq": "==",
  33    "ne": "!=",
  34    "gt": ">",
  35    "gteq": ">=",
  36    "lt": "<",
  37    "lteq": "<=",
  38    "in": "in",
  39    "notin": "not in",
  40}
  41
  42
  43def optimizeconst(f: F) -> F:
  44    def new_func(
  45        self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
  46    ) -> t.Any:
  47        # Only optimize if the frame is not volatile
  48        if self.optimizer is not None and not frame.eval_ctx.volatile:
  49            new_node = self.optimizer.visit(node, frame.eval_ctx)
  50
  51            if new_node != node:
  52                return self.visit(new_node, frame)
  53
  54        return f(self, node, frame, **kwargs)
  55
  56    return update_wrapper(t.cast(F, new_func), f)
  57
  58
  59def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]:
  60    @optimizeconst
  61    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
  62        if (
  63            self.environment.sandboxed
  64            and op in self.environment.intercepted_binops  # type: ignore
  65        ):
  66            self.write(f"environment.call_binop(context, {op!r}, ")
  67            self.visit(node.left, frame)
  68            self.write(", ")
  69            self.visit(node.right, frame)
  70        else:
  71            self.write("(")
  72            self.visit(node.left, frame)
  73            self.write(f" {op} ")
  74            self.visit(node.right, frame)
  75
  76        self.write(")")
  77
  78    return visitor
  79
  80
  81def _make_unop(
  82    op: str,
  83) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]:
  84    @optimizeconst
  85    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
  86        if (
  87            self.environment.sandboxed
  88            and op in self.environment.intercepted_unops  # type: ignore
  89        ):
  90            self.write(f"environment.call_unop(context, {op!r}, ")
  91            self.visit(node.node, frame)
  92        else:
  93            self.write("(" + op)
  94            self.visit(node.node, frame)
  95
  96        self.write(")")
  97
  98    return visitor
  99
 100
 101def generate(
 102    node: nodes.Template,
 103    environment: "Environment",
 104    name: t.Optional[str],
 105    filename: t.Optional[str],
 106    stream: t.Optional[t.TextIO] = None,
 107    defer_init: bool = False,
 108    optimized: bool = True,
 109) -> t.Optional[str]:
 110    """Generate the python source for a node tree."""
 111    if not isinstance(node, nodes.Template):
 112        raise TypeError("Can't compile non template nodes")
 113
 114    generator = environment.code_generator_class(
 115        environment, name, filename, stream, defer_init, optimized
 116    )
 117    generator.visit(node)
 118
 119    if stream is None:
 120        return generator.stream.getvalue()  # type: ignore
 121
 122    return None
 123
 124
 125def has_safe_repr(value: t.Any) -> bool:
 126    """Does the node have a safe representation?"""
 127    if value is None or value is NotImplemented or value is Ellipsis:
 128        return True
 129
 130    if type(value) in {bool, int, float, complex, range, str, Markup}:
 131        return True
 132
 133    if type(value) in {tuple, list, set, frozenset}:
 134        return all(has_safe_repr(v) for v in value)
 135
 136    if type(value) is dict:
 137        return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
 138
 139    return False
 140
 141
 142def find_undeclared(
 143    nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
 144) -> t.Set[str]:
 145    """Check if the names passed are accessed undeclared.  The return value
 146    is a set of all the undeclared names from the sequence of names found.
 147    """
 148    visitor = UndeclaredNameVisitor(names)
 149    try:
 150        for node in nodes:
 151            visitor.visit(node)
 152    except VisitorExit:
 153        pass
 154    return visitor.undeclared
 155
 156
 157class MacroRef:
 158    def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
 159        self.node = node
 160        self.accesses_caller = False
 161        self.accesses_kwargs = False
 162        self.accesses_varargs = False
 163
 164
 165class Frame:
 166    """Holds compile time information for us."""
 167
 168    def __init__(
 169        self,
 170        eval_ctx: EvalContext,
 171        parent: t.Optional["Frame"] = None,
 172        level: t.Optional[int] = None,
 173    ) -> None:
 174        self.eval_ctx = eval_ctx
 175
 176        # the parent of this frame
 177        self.parent = parent
 178
 179        if parent is None:
 180            self.symbols = Symbols(level=level)
 181
 182            # in some dynamic inheritance situations the compiler needs to add
 183            # write tests around output statements.
 184            self.require_output_check = False
 185
 186            # inside some tags we are using a buffer rather than yield statements.
 187            # this for example affects {% filter %} or {% macro %}.  If a frame
 188            # is buffered this variable points to the name of the list used as
 189            # buffer.
 190            self.buffer: t.Optional[str] = None
 191
 192            # the name of the block we're in, otherwise None.
 193            self.block: t.Optional[str] = None
 194
 195        else:
 196            self.symbols = Symbols(parent.symbols, level=level)
 197            self.require_output_check = parent.require_output_check
 198            self.buffer = parent.buffer
 199            self.block = parent.block
 200
 201        # a toplevel frame is the root + soft frames such as if conditions.
 202        self.toplevel = False
 203
 204        # the root frame is basically just the outermost frame, so no if
 205        # conditions.  This information is used to optimize inheritance
 206        # situations.
 207        self.rootlevel = False
 208
 209        # variables set inside of loops and blocks should not affect outer frames,
 210        # but they still needs to be kept track of as part of the active context.
 211        self.loop_frame = False
 212        self.block_frame = False
 213
 214        # track whether the frame is being used in an if-statement or conditional
 215        # expression as it determines which errors should be raised during runtime
 216        # or compile time.
 217        self.soft_frame = False
 218
 219    def copy(self) -> "Frame":
 220        """Create a copy of the current one."""
 221        rv = object.__new__(self.__class__)
 222        rv.__dict__.update(self.__dict__)
 223        rv.symbols = self.symbols.copy()
 224        return rv
 225
 226    def inner(self, isolated: bool = False) -> "Frame":
 227        """Return an inner frame."""
 228        if isolated:
 229            return Frame(self.eval_ctx, level=self.symbols.level + 1)
 230        return Frame(self.eval_ctx, self)
 231
 232    def soft(self) -> "Frame":
 233        """Return a soft frame.  A soft frame may not be modified as
 234        standalone thing as it shares the resources with the frame it
 235        was created of, but it's not a rootlevel frame any longer.
 236
 237        This is only used to implement if-statements and conditional
 238        expressions.
 239        """
 240        rv = self.copy()
 241        rv.rootlevel = False
 242        rv.soft_frame = True
 243        return rv
 244
 245    __copy__ = copy
 246
 247
 248class VisitorExit(RuntimeError):
 249    """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
 250
 251
 252class DependencyFinderVisitor(NodeVisitor):
 253    """A visitor that collects filter and test calls."""
 254
 255    def __init__(self) -> None:
 256        self.filters: t.Set[str] = set()
 257        self.tests: t.Set[str] = set()
 258
 259    def visit_Filter(self, node: nodes.Filter) -> None:
 260        self.generic_visit(node)
 261        self.filters.add(node.name)
 262
 263    def visit_Test(self, node: nodes.Test) -> None:
 264        self.generic_visit(node)
 265        self.tests.add(node.name)
 266
 267    def visit_Block(self, node: nodes.Block) -> None:
 268        """Stop visiting at blocks."""
 269
 270
 271class UndeclaredNameVisitor(NodeVisitor):
 272    """A visitor that checks if a name is accessed without being
 273    declared.  This is different from the frame visitor as it will
 274    not stop at closure frames.
 275    """
 276
 277    def __init__(self, names: t.Iterable[str]) -> None:
 278        self.names = set(names)
 279        self.undeclared: t.Set[str] = set()
 280
 281    def visit_Name(self, node: nodes.Name) -> None:
 282        if node.ctx == "load" and node.name in self.names:
 283            self.undeclared.add(node.name)
 284            if self.undeclared == self.names:
 285                raise VisitorExit()
 286        else:
 287            self.names.discard(node.name)
 288
 289    def visit_Block(self, node: nodes.Block) -> None:
 290        """Stop visiting a blocks."""
 291
 292
 293class CompilerExit(Exception):
 294    """Raised if the compiler encountered a situation where it just
 295    doesn't make sense to further process the code.  Any block that
 296    raises such an exception is not further processed.
 297    """
 298
 299
 300class CodeGenerator(NodeVisitor):
 301    def __init__(
 302        self,
 303        environment: "Environment",
 304        name: t.Optional[str],
 305        filename: t.Optional[str],
 306        stream: t.Optional[t.TextIO] = None,
 307        defer_init: bool = False,
 308        optimized: bool = True,
 309    ) -> None:
 310        if stream is None:
 311            stream = StringIO()
 312        self.environment = environment
 313        self.name = name
 314        self.filename = filename
 315        self.stream = stream
 316        self.created_block_context = False
 317        self.defer_init = defer_init
 318        self.optimizer: t.Optional[Optimizer] = None
 319
 320        if optimized:
 321            self.optimizer = Optimizer(environment)
 322
 323        # aliases for imports
 324        self.import_aliases: t.Dict[str, str] = {}
 325
 326        # a registry for all blocks.  Because blocks are moved out
 327        # into the global python scope they are registered here
 328        self.blocks: t.Dict[str, nodes.Block] = {}
 329
 330        # the number of extends statements so far
 331        self.extends_so_far = 0
 332
 333        # some templates have a rootlevel extends.  In this case we
 334        # can safely assume that we're a child template and do some
 335        # more optimizations.
 336        self.has_known_extends = False
 337
 338        # the current line number
 339        self.code_lineno = 1
 340
 341        # registry of all filters and tests (global, not block local)
 342        self.tests: t.Dict[str, str] = {}
 343        self.filters: t.Dict[str, str] = {}
 344
 345        # the debug information
 346        self.debug_info: t.List[t.Tuple[int, int]] = []
 347        self._write_debug_info: t.Optional[int] = None
 348
 349        # the number of new lines before the next write()
 350        self._new_lines = 0
 351
 352        # the line number of the last written statement
 353        self._last_line = 0
 354
 355        # true if nothing was written so far.
 356        self._first_write = True
 357
 358        # used by the `temporary_identifier` method to get new
 359        # unique, temporary identifier
 360        self._last_identifier = 0
 361
 362        # the current indentation
 363        self._indentation = 0
 364
 365        # Tracks toplevel assignments
 366        self._assign_stack: t.List[t.Set[str]] = []
 367
 368        # Tracks parameter definition blocks
 369        self._param_def_block: t.List[t.Set[str]] = []
 370
 371        # Tracks the current context.
 372        self._context_reference_stack = ["context"]
 373
 374    @property
 375    def optimized(self) -> bool:
 376        return self.optimizer is not None
 377
 378    # -- Various compilation helpers
 379
 380    def fail(self, msg: str, lineno: int) -> "te.NoReturn":
 381        """Fail with a :exc:`TemplateAssertionError`."""
 382        raise TemplateAssertionError(msg, lineno, self.name, self.filename)
 383
 384    def temporary_identifier(self) -> str:
 385        """Get a new unique identifier."""
 386        self._last_identifier += 1
 387        return f"t_{self._last_identifier}"
 388
 389    def buffer(self, frame: Frame) -> None:
 390        """Enable buffering for the frame from that point onwards."""
 391        frame.buffer = self.temporary_identifier()
 392        self.writeline(f"{frame.buffer} = []")
 393
 394    def return_buffer_contents(
 395        self, frame: Frame, force_unescaped: bool = False
 396    ) -> None:
 397        """Return the buffer contents of the frame."""
 398        if not force_unescaped:
 399            if frame.eval_ctx.volatile:
 400                self.writeline("if context.eval_ctx.autoescape:")
 401                self.indent()
 402                self.writeline(f"return Markup(concat({frame.buffer}))")
 403                self.outdent()
 404                self.writeline("else:")
 405                self.indent()
 406                self.writeline(f"return concat({frame.buffer})")
 407                self.outdent()
 408                return
 409            elif frame.eval_ctx.autoescape:
 410                self.writeline(f"return Markup(concat({frame.buffer}))")
 411                return
 412        self.writeline(f"return concat({frame.buffer})")
 413
 414    def indent(self) -> None:
 415        """Indent by one."""
 416        self._indentation += 1
 417
 418    def outdent(self, step: int = 1) -> None:
 419        """Outdent by step."""
 420        self._indentation -= step
 421
 422    def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
 423        """Yield or write into the frame buffer."""
 424        if frame.buffer is None:
 425            self.writeline("yield ", node)
 426        else:
 427            self.writeline(f"{frame.buffer}.append(", node)
 428
 429    def end_write(self, frame: Frame) -> None:
 430        """End the writing process started by `start_write`."""
 431        if frame.buffer is not None:
 432            self.write(")")
 433
 434    def simple_write(
 435        self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
 436    ) -> None:
 437        """Simple shortcut for start_write + write + end_write."""
 438        self.start_write(frame, node)
 439        self.write(s)
 440        self.end_write(frame)
 441
 442    def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
 443        """Visit a list of nodes as block in a frame.  If the current frame
 444        is no buffer a dummy ``if 0: yield None`` is written automatically.
 445        """
 446        try:
 447            self.writeline("pass")
 448            for node in nodes:
 449                self.visit(node, frame)
 450        except CompilerExit:
 451            pass
 452
 453    def write(self, x: str) -> None:
 454        """Write a string into the output stream."""
 455        if self._new_lines:
 456            if not self._first_write:
 457                self.stream.write("\n" * self._new_lines)
 458                self.code_lineno += self._new_lines
 459                if self._write_debug_info is not None:
 460                    self.debug_info.append((self._write_debug_info, self.code_lineno))
 461                    self._write_debug_info = None
 462            self._first_write = False
 463            self.stream.write("    " * self._indentation)
 464            self._new_lines = 0
 465        self.stream.write(x)
 466
 467    def writeline(
 468        self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
 469    ) -> None:
 470        """Combination of newline and write."""
 471        self.newline(node, extra)
 472        self.write(x)
 473
 474    def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
 475        """Add one or more newlines before the next write."""
 476        self._new_lines = max(self._new_lines, 1 + extra)
 477        if node is not None and node.lineno != self._last_line:
 478            self._write_debug_info = node.lineno
 479            self._last_line = node.lineno
 480
 481    def signature(
 482        self,
 483        node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
 484        frame: Frame,
 485        extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
 486    ) -> None:
 487        """Writes a function call to the stream for the current node.
 488        A leading comma is added automatically.  The extra keyword
 489        arguments may not include python keywords otherwise a syntax
 490        error could occur.  The extra keyword arguments should be given
 491        as python dict.
 492        """
 493        # if any of the given keyword arguments is a python keyword
 494        # we have to make sure that no invalid call is created.
 495        kwarg_workaround = any(
 496            is_python_keyword(t.cast(str, k))
 497            for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
 498        )
 499
 500        for arg in node.args:
 501            self.write(", ")
 502            self.visit(arg, frame)
 503
 504        if not kwarg_workaround:
 505            for kwarg in node.kwargs:
 506                self.write(", ")
 507                self.visit(kwarg, frame)
 508            if extra_kwargs is not None:
 509                for key, value in extra_kwargs.items():
 510                    self.write(f", {key}={value}")
 511        if node.dyn_args:
 512            self.write(", *")
 513            self.visit(node.dyn_args, frame)
 514
 515        if kwarg_workaround:
 516            if node.dyn_kwargs is not None:
 517                self.write(", **dict({")
 518            else:
 519                self.write(", **{")
 520            for kwarg in node.kwargs:
 521                self.write(f"{kwarg.key!r}: ")
 522                self.visit(kwarg.value, frame)
 523                self.write(", ")
 524            if extra_kwargs is not None:
 525                for key, value in extra_kwargs.items():
 526                    self.write(f"{key!r}: {value}, ")
 527            if node.dyn_kwargs is not None:
 528                self.write("}, **")
 529                self.visit(node.dyn_kwargs, frame)
 530                self.write(")")
 531            else:
 532                self.write("}")
 533
 534        elif node.dyn_kwargs is not None:
 535            self.write(", **")
 536            self.visit(node.dyn_kwargs, frame)
 537
 538    def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
 539        """Find all filter and test names used in the template and
 540        assign them to variables in the compiled namespace. Checking
 541        that the names are registered with the environment is done when
 542        compiling the Filter and Test nodes. If the node is in an If or
 543        CondExpr node, the check is done at runtime instead.
 544
 545        .. versionchanged:: 3.0
 546            Filters and tests in If and CondExpr nodes are checked at
 547            runtime instead of compile time.
 548        """
 549        visitor = DependencyFinderVisitor()
 550
 551        for node in nodes:
 552            visitor.visit(node)
 553
 554        for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
 555            self.tests,
 556            visitor.tests,
 557            "tests",
 558        ):
 559            for name in sorted(names):
 560                if name not in id_map:
 561                    id_map[name] = self.temporary_identifier()
 562
 563                # add check during runtime that dependencies used inside of executed
 564                # blocks are defined, as this step may be skipped during compile time
 565                self.writeline("try:")
 566                self.indent()
 567                self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
 568                self.outdent()
 569                self.writeline("except KeyError:")
 570                self.indent()
 571                self.writeline("@internalcode")
 572                self.writeline(f"def {id_map[name]}(*unused):")
 573                self.indent()
 574                self.writeline(
 575                    f'raise TemplateRuntimeError("No {dependency[:-1]}'
 576                    f' named {name!r} found.")'
 577                )
 578                self.outdent()
 579                self.outdent()
 580
 581    def enter_frame(self, frame: Frame) -> None:
 582        undefs = []
 583        for target, (action, param) in frame.symbols.loads.items():
 584            if action == VAR_LOAD_PARAMETER:
 585                pass
 586            elif action == VAR_LOAD_RESOLVE:
 587                self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
 588            elif action == VAR_LOAD_ALIAS:
 589                self.writeline(f"{target} = {param}")
 590            elif action == VAR_LOAD_UNDEFINED:
 591                undefs.append(target)
 592            else:
 593                raise NotImplementedError("unknown load instruction")
 594        if undefs:
 595            self.writeline(f"{' = '.join(undefs)} = missing")
 596
 597    def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
 598        if not with_python_scope:
 599            undefs = []
 600            for target in frame.symbols.loads:
 601                undefs.append(target)
 602            if undefs:
 603                self.writeline(f"{' = '.join(undefs)} = missing")
 604
 605    def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
 606        return async_value if self.environment.is_async else sync_value
 607
 608    def func(self, name: str) -> str:
 609        return f"{self.choose_async()}def {name}"
 610
 611    def macro_body(
 612        self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
 613    ) -> t.Tuple[Frame, MacroRef]:
 614        """Dump the function def of a macro or call block."""
 615        frame = frame.inner()
 616        frame.symbols.analyze_node(node)
 617        macro_ref = MacroRef(node)
 618
 619        explicit_caller = None
 620        skip_special_params = set()
 621        args = []
 622
 623        for idx, arg in enumerate(node.args):
 624            if arg.name == "caller":
 625                explicit_caller = idx
 626            if arg.name in ("kwargs", "varargs"):
 627                skip_special_params.add(arg.name)
 628            args.append(frame.symbols.ref(arg.name))
 629
 630        undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
 631
 632        if "caller" in undeclared:
 633            # In older Jinja versions there was a bug that allowed caller
 634            # to retain the special behavior even if it was mentioned in
 635            # the argument list.  However thankfully this was only really
 636            # working if it was the last argument.  So we are explicitly
 637            # checking this now and error out if it is anywhere else in
 638            # the argument list.
 639            if explicit_caller is not None:
 640                try:
 641                    node.defaults[explicit_caller - len(node.args)]
 642                except IndexError:
 643                    self.fail(
 644                        "When defining macros or call blocks the "
 645                        'special "caller" argument must be omitted '
 646                        "or be given a default.",
 647                        node.lineno,
 648                    )
 649            else:
 650                args.append(frame.symbols.declare_parameter("caller"))
 651            macro_ref.accesses_caller = True
 652        if "kwargs" in undeclared and "kwargs" not in skip_special_params:
 653            args.append(frame.symbols.declare_parameter("kwargs"))
 654            macro_ref.accesses_kwargs = True
 655        if "varargs" in undeclared and "varargs" not in skip_special_params:
 656            args.append(frame.symbols.declare_parameter("varargs"))
 657            macro_ref.accesses_varargs = True
 658
 659        # macros are delayed, they never require output checks
 660        frame.require_output_check = False
 661        frame.symbols.analyze_node(node)
 662        self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
 663        self.indent()
 664
 665        self.buffer(frame)
 666        self.enter_frame(frame)
 667
 668        self.push_parameter_definitions(frame)
 669        for idx, arg in enumerate(node.args):
 670            ref = frame.symbols.ref(arg.name)
 671            self.writeline(f"if {ref} is missing:")
 672            self.indent()
 673            try:
 674                default = node.defaults[idx - len(node.args)]
 675            except IndexError:
 676                self.writeline(
 677                    f'{ref} = undefined("parameter {arg.name!r} was not provided",'
 678                    f" name={arg.name!r})"
 679                )
 680            else:
 681                self.writeline(f"{ref} = ")
 682                self.visit(default, frame)
 683            self.mark_parameter_stored(ref)
 684            self.outdent()
 685        self.pop_parameter_definitions()
 686
 687        self.blockvisit(node.body, frame)
 688        self.return_buffer_contents(frame, force_unescaped=True)
 689        self.leave_frame(frame, with_python_scope=True)
 690        self.outdent()
 691
 692        return frame, macro_ref
 693
 694    def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
 695        """Dump the macro definition for the def created by macro_body."""
 696        arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
 697        name = getattr(macro_ref.node, "name", None)
 698        if len(macro_ref.node.args) == 1:
 699            arg_tuple += ","
 700        self.write(
 701            f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
 702            f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
 703            f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
 704        )
 705
 706    def position(self, node: nodes.Node) -> str:
 707        """Return a human readable position for the node."""
 708        rv = f"line {node.lineno}"
 709        if self.name is not None:
 710            rv = f"{rv} in {self.name!r}"
 711        return rv
 712
 713    def dump_local_context(self, frame: Frame) -> str:
 714        items_kv = ", ".join(
 715            f"{name!r}: {target}"
 716            for name, target in frame.symbols.dump_stores().items()
 717        )
 718        return f"{{{items_kv}}}"
 719
 720    def write_commons(self) -> None:
 721        """Writes a common preamble that is used by root and block functions.
 722        Primarily this sets up common local helpers and enforces a generator
 723        through a dead branch.
 724        """
 725        self.writeline("resolve = context.resolve_or_missing")
 726        self.writeline("undefined = environment.undefined")
 727        self.writeline("concat = environment.concat")
 728        # always use the standard Undefined class for the implicit else of
 729        # conditional expressions
 730        self.writeline("cond_expr_undefined = Undefined")
 731        self.writeline("if 0: yield None")
 732
 733    def push_parameter_definitions(self, frame: Frame) -> None:
 734        """Pushes all parameter targets from the given frame into a local
 735        stack that permits tracking of yet to be assigned parameters.  In
 736        particular this enables the optimization from `visit_Name` to skip
 737        undefined expressions for parameters in macros as macros can reference
 738        otherwise unbound parameters.
 739        """
 740        self._param_def_block.append(frame.symbols.dump_param_targets())
 741
 742    def pop_parameter_definitions(self) -> None:
 743        """Pops the current parameter definitions set."""
 744        self._param_def_block.pop()
 745
 746    def mark_parameter_stored(self, target: str) -> None:
 747        """Marks a parameter in the current parameter definitions as stored.
 748        This will skip the enforced undefined checks.
 749        """
 750        if self._param_def_block:
 751            self._param_def_block[-1].discard(target)
 752
 753    def push_context_reference(self, target: str) -> None:
 754        self._context_reference_stack.append(target)
 755
 756    def pop_context_reference(self) -> None:
 757        self._context_reference_stack.pop()
 758
 759    def get_context_ref(self) -> str:
 760        return self._context_reference_stack[-1]
 761
 762    def get_resolve_func(self) -> str:
 763        target = self._context_reference_stack[-1]
 764        if target == "context":
 765            return "resolve"
 766        return f"{target}.resolve"
 767
 768    def derive_context(self, frame: Frame) -> str:
 769        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
 770
 771    def parameter_is_undeclared(self, target: str) -> bool:
 772        """Checks if a given target is an undeclared parameter."""
 773        if not self._param_def_block:
 774            return False
 775        return target in self._param_def_block[-1]
 776
 777    def push_assign_tracking(self) -> None:
 778        """Pushes a new layer for assignment tracking."""
 779        self._assign_stack.append(set())
 780
 781    def pop_assign_tracking(self, frame: Frame) -> None:
 782        """Pops the topmost level for assignment tracking and updates the
 783        context variables if necessary.
 784        """
 785        vars = self._assign_stack.pop()
 786        if (
 787            not frame.block_frame
 788            and not frame.loop_frame
 789            and not frame.toplevel
 790            or not vars
 791        ):
 792            return
 793        public_names = [x for x in vars if x[:1] != "_"]
 794        if len(vars) == 1:
 795            name = next(iter(vars))
 796            ref = frame.symbols.ref(name)
 797            if frame.loop_frame:
 798                self.writeline(f"_loop_vars[{name!r}] = {ref}")
 799                return
 800            if frame.block_frame:
 801                self.writeline(f"_block_vars[{name!r}] = {ref}")
 802                return
 803            self.writeline(f"context.vars[{name!r}] = {ref}")
 804        else:
 805            if frame.loop_frame:
 806                self.writeline("_loop_vars.update({")
 807            elif frame.block_frame:
 808                self.writeline("_block_vars.update({")
 809            else:
 810                self.writeline("context.vars.update({")
 811            for idx, name in enumerate(vars):
 812                if idx:
 813                    self.write(", ")
 814                ref = frame.symbols.ref(name)
 815                self.write(f"{name!r}: {ref}")
 816            self.write("})")
 817        if not frame.block_frame and not frame.loop_frame and public_names:
 818            if len(public_names) == 1:
 819                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
 820            else:
 821                names_str = ", ".join(map(repr, public_names))
 822                self.writeline(f"context.exported_vars.update(({names_str}))")
 823
 824    # -- Statement Visitors
 825
 826    def visit_Template(
 827        self, node: nodes.Template, frame: t.Optional[Frame] = None
 828    ) -> None:
 829        assert frame is None, "no root frame allowed"
 830        eval_ctx = EvalContext(self.environment, self.name)
 831
 832        from .runtime import exported, async_exported
 833
 834        if self.environment.is_async:
 835            exported_names = sorted(exported + async_exported)
 836        else:
 837            exported_names = sorted(exported)
 838
 839        self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
 840
 841        # if we want a deferred initialization we cannot move the
 842        # environment into a local name
 843        envenv = "" if self.defer_init else ", environment=environment"
 844
 845        # do we have an extends tag at all?  If not, we can save some
 846        # overhead by just not processing any inheritance code.
 847        have_extends = node.find(nodes.Extends) is not None
 848
 849        # find all blocks
 850        for block in node.find_all(nodes.Block):
 851            if block.name in self.blocks:
 852                self.fail(f"block {block.name!r} defined twice", block.lineno)
 853            self.blocks[block.name] = block
 854
 855        # find all imports and import them
 856        for import_ in node.find_all(nodes.ImportedName):
 857            if import_.importname not in self.import_aliases:
 858                imp = import_.importname
 859                self.import_aliases[imp] = alias = self.temporary_identifier()
 860                if "." in imp:
 861                    module, obj = imp.rsplit(".", 1)
 862                    self.writeline(f"from {module} import {obj} as {alias}")
 863                else:
 864                    self.writeline(f"import {imp} as {alias}")
 865
 866        # add the load name
 867        self.writeline(f"name = {self.name!r}")
 868
 869        # generate the root render function.
 870        self.writeline(
 871            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
 872        )
 873        self.indent()
 874        self.write_commons()
 875
 876        # process the root
 877        frame = Frame(eval_ctx)
 878        if "self" in find_undeclared(node.body, ("self",)):
 879            ref = frame.symbols.declare_parameter("self")
 880            self.writeline(f"{ref} = TemplateReference(context)")
 881        frame.symbols.analyze_node(node)
 882        frame.toplevel = frame.rootlevel = True
 883        frame.require_output_check = have_extends and not self.has_known_extends
 884        if have_extends:
 885            self.writeline("parent_template = None")
 886        self.enter_frame(frame)
 887        self.pull_dependencies(node.body)
 888        self.blockvisit(node.body, frame)
 889        self.leave_frame(frame, with_python_scope=True)
 890        self.outdent()
 891
 892        # make sure that the parent root is called.
 893        if have_extends:
 894            if not self.has_known_extends:
 895                self.indent()
 896                self.writeline("if parent_template is not None:")
 897            self.indent()
 898            if not self.environment.is_async:
 899                self.writeline("yield from parent_template.root_render_func(context)")
 900            else:
 901                self.writeline(
 902                    "async for event in parent_template.root_render_func(context):"
 903                )
 904                self.indent()
 905                self.writeline("yield event")
 906                self.outdent()
 907            self.outdent(1 + (not self.has_known_extends))
 908
 909        # at this point we now have the blocks collected and can visit them too.
 910        for name, block in self.blocks.items():
 911            self.writeline(
 912                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
 913                block,
 914                1,
 915            )
 916            self.indent()
 917            self.write_commons()
 918            # It's important that we do not make this frame a child of the
 919            # toplevel template.  This would cause a variety of
 920            # interesting issues with identifier tracking.
 921            block_frame = Frame(eval_ctx)
 922            block_frame.block_frame = True
 923            undeclared = find_undeclared(block.body, ("self", "super"))
 924            if "self" in undeclared:
 925                ref = block_frame.symbols.declare_parameter("self")
 926                self.writeline(f"{ref} = TemplateReference(context)")
 927            if "super" in undeclared:
 928                ref = block_frame.symbols.declare_parameter("super")
 929                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
 930            block_frame.symbols.analyze_node(block)
 931            block_frame.block = name
 932            self.writeline("_block_vars = {}")
 933            self.enter_frame(block_frame)
 934            self.pull_dependencies(block.body)
 935            self.blockvisit(block.body, block_frame)
 936            self.leave_frame(block_frame, with_python_scope=True)
 937            self.outdent()
 938
 939        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
 940        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
 941        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
 942        self.writeline(f"debug_info = {debug_kv_str!r}")
 943
 944    def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
 945        """Call a block and register it for the template."""
 946        level = 0
 947        if frame.toplevel:
 948            # if we know that we are a child template, there is no need to
 949            # check if we are one
 950            if self.has_known_extends:
 951                return
 952            if self.extends_so_far > 0:
 953                self.writeline("if parent_template is None:")
 954                self.indent()
 955                level += 1
 956
 957        if node.scoped:
 958            context = self.derive_context(frame)
 959        else:
 960            context = self.get_context_ref()
 961
 962        if node.required:
 963            self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
 964            self.indent()
 965            self.writeline(
 966                f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
 967                node,
 968            )
 969            self.outdent()
 970
 971        if not self.environment.is_async and frame.buffer is None:
 972            self.writeline(
 973                f"yield from context.blocks[{node.name!r}][0]({context})", node
 974            )
 975        else:
 976            self.writeline(
 977                f"{self.choose_async()}for event in"
 978                f" context.blocks[{node.name!r}][0]({context}):",
 979                node,
 980            )
 981            self.indent()
 982            self.simple_write("event", frame)
 983            self.outdent()
 984
 985        self.outdent(level)
 986
 987    def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
 988        """Calls the extender."""
 989        if not frame.toplevel:
 990            self.fail("cannot use extend from a non top-level scope", node.lineno)
 991
 992        # if the number of extends statements in general is zero so
 993        # far, we don't have to add a check if something extended
 994        # the template before this one.
 995        if self.extends_so_far > 0:
 996            # if we have a known extends we just add a template runtime
 997            # error into the generated code.  We could catch that at compile
 998            # time too, but i welcome it not to confuse users by throwing the
 999            # same error at different times just "because we can".
1000            if not self.has_known_extends:
1001                self.writeline("if parent_template is not None:")
1002                self.indent()
1003            self.writeline('raise TemplateRuntimeError("extended multiple times")')
1004
1005            # if we have a known extends already we don't need that code here
1006            # as we know that the template execution will end here.
1007            if self.has_known_extends:
1008                raise CompilerExit()
1009            else:
1010                self.outdent()
1011
1012        self.writeline("parent_template = environment.get_template(", node)
1013        self.visit(node.template, frame)
1014        self.write(f", {self.name!r})")
1015        self.writeline("for name, parent_block in parent_template.blocks.items():")
1016        self.indent()
1017        self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
1018        self.outdent()
1019
1020        # if this extends statement was in the root level we can take
1021        # advantage of that information and simplify the generated code
1022        # in the top level from this point onwards
1023        if frame.rootlevel:
1024            self.has_known_extends = True
1025
1026        # and now we have one more
1027        self.extends_so_far += 1
1028
1029    def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1030        """Handles includes."""
1031        if node.ignore_missing:
1032            self.writeline("try:")
1033            self.indent()
1034
1035        func_name = "get_or_select_template"
1036        if isinstance(node.template, nodes.Const):
1037            if isinstance(node.template.value, str):
1038                func_name = "get_template"
1039            elif isinstance(node.template.value, (tuple, list)):
1040                func_name = "select_template"
1041        elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1042            func_name = "select_template"
1043
1044        self.writeline(f"template = environment.{func_name}(", node)
1045        self.visit(node.template, frame)
1046        self.write(f", {self.name!r})")
1047        if node.ignore_missing:
1048            self.outdent()
1049            self.writeline("except TemplateNotFound:")
1050            self.indent()
1051            self.writeline("pass")
1052            self.outdent()
1053            self.writeline("else:")
1054            self.indent()
1055
1056        skip_event_yield = False
1057        if node.with_context:
1058            self.writeline(
1059                f"{self.choose_async()}for event in template.root_render_func("
1060                "template.new_context(context.get_all(), True,"
1061                f" {self.dump_local_context(frame)})):"
1062            )
1063        elif self.environment.is_async:
1064            self.writeline(
1065                "for event in (await template._get_default_module_async())"
1066                "._body_stream:"
1067            )
1068        else:
1069            self.writeline("yield from template._get_default_module()._body_stream")
1070            skip_event_yield = True
1071
1072        if not skip_event_yield:
1073            self.indent()
1074            self.simple_write("event", frame)
1075            self.outdent()
1076
1077        if node.ignore_missing:
1078            self.outdent()
1079
1080    def _import_common(
1081        self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
1082    ) -> None:
1083        self.write(f"{self.choose_async('await ')}environment.get_template(")
1084        self.visit(node.template, frame)
1085        self.write(f", {self.name!r}).")
1086
1087        if node.with_context:
1088            f_name = f"make_module{self.choose_async('_async')}"
1089            self.write(
1090                f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
1091            )
1092        else:
1093            self.write(f"_get_default_module{self.choose_async('_async')}(context)")
1094
1095    def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1096        """Visit regular imports."""
1097        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1098        if frame.toplevel:
1099            self.write(f"context.vars[{node.target!r}] = ")
1100
1101        self._import_common(node, frame)
1102
1103        if frame.toplevel and not node.target.startswith("_"):
1104            self.writeline(f"context.exported_vars.discard({node.target!r})")
1105
1106    def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1107        """Visit named imports."""
1108        self.newline(node)
1109        self.write("included_template = ")
1110        self._import_common(node, frame)
1111        var_names = []
1112        discarded_names = []
1113        for name in node.names:
1114            if isinstance(name, tuple):
1115                name, alias = name
1116            else:
1117                alias = name
1118            self.writeline(
1119                f"{frame.symbols.ref(alias)} ="
1120                f" getattr(included_template, {name!r}, missing)"
1121            )
1122            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1123            self.indent()
1124            message = (
1125                "the template {included_template.__name__!r}"
1126                f" (imported on {self.position(node)})"
1127                f" does not export the requested name {name!r}"
1128            )
1129            self.writeline(
1130                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1131            )
1132            self.outdent()
1133            if frame.toplevel:
1134                var_names.append(alias)
1135                if not alias.startswith("_"):
1136                    discarded_names.append(alias)
1137
1138        if var_names:
1139            if len(var_names) == 1:
1140                name = var_names[0]
1141                self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1142            else:
1143                names_kv = ", ".join(
1144                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1145                )
1146                self.writeline(f"context.vars.update({{{names_kv}}})")
1147        if discarded_names:
1148            if len(discarded_names) == 1:
1149                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1150            else:
1151                names_str = ", ".join(map(repr, discarded_names))
1152                self.writeline(
1153                    f"context.exported_vars.difference_update(({names_str}))"
1154                )
1155
1156    def visit_For(self, node: nodes.For, frame: Frame) -> None:
1157        loop_frame = frame.inner()
1158        loop_frame.loop_frame = True
1159        test_frame = frame.inner()
1160        else_frame = frame.inner()
1161
1162        # try to figure out if we have an extended loop.  An extended loop
1163        # is necessary if the loop is in recursive mode if the special loop
1164        # variable is accessed in the body if the body is a scoped block.
1165        extended_loop = (
1166            node.recursive
1167            or "loop"
1168            in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1169            or any(block.scoped for block in node.find_all(nodes.Block))
1170        )
1171
1172        loop_ref = None
1173        if extended_loop:
1174            loop_ref = loop_frame.symbols.declare_parameter("loop")
1175
1176        loop_frame.symbols.analyze_node(node, for_branch="body")
1177        if node.else_:
1178            else_frame.symbols.analyze_node(node, for_branch="else")
1179
1180        if node.test:
1181            loop_filter_func = self.temporary_identifier()
1182            test_frame.symbols.analyze_node(node, for_branch="test")
1183            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1184            self.indent()
1185            self.enter_frame(test_frame)
1186            self.writeline(self.choose_async("async for ", "for "))
1187            self.visit(node.target, loop_frame)
1188            self.write(" in ")
1189            self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1190            self.write(":")
1191            self.indent()
1192            self.writeline("if ", node.test)
1193            self.visit(node.test, test_frame)
1194            self.write(":")
1195            self.indent()
1196            self.writeline("yield ")
1197            self.visit(node.target, loop_frame)
1198            self.outdent(3)
1199            self.leave_frame(test_frame, with_python_scope=True)
1200
1201        # if we don't have an recursive loop we have to find the shadowed
1202        # variables at that point.  Because loops can be nested but the loop
1203        # variable is a special one we have to enforce aliasing for it.
1204        if node.recursive:
1205            self.writeline(
1206                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1207            )
1208            self.indent()
1209            self.buffer(loop_frame)
1210
1211            # Use the same buffer for the else frame
1212            else_frame.buffer = loop_frame.buffer
1213
1214        # make sure the loop variable is a special one and raise a template
1215        # assertion error if a loop tries to write to loop
1216        if extended_loop:
1217            self.writeline(f"{loop_ref} = missing")
1218
1219        for name in node.find_all(nodes.Name):
1220            if name.ctx == "store" and name.name == "loop":
1221                self.fail(
1222                    "Can't assign to special loop variable in for-loop target",
1223                    name.lineno,
1224                )
1225
1226        if node.else_:
1227            iteration_indicator = self.temporary_identifier()
1228            self.writeline(f"{iteration_indicator} = 1")
1229
1230        self.writeline(self.choose_async("async for ", "for "), node)
1231        self.visit(node.target, loop_frame)
1232        if extended_loop:
1233            self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1234        else:
1235            self.write(" in ")
1236
1237        if node.test:
1238            self.write(f"{loop_filter_func}(")
1239        if node.recursive:
1240            self.write("reciter")
1241        else:
1242            if self.environment.is_async and not extended_loop:
1243                self.write("auto_aiter(")
1244            self.visit(node.iter, frame)
1245            if self.environment.is_async and not extended_loop:
1246                self.write(")")
1247        if node.test:
1248            self.write(")")
1249
1250        if node.recursive:
1251            self.write(", undefined, loop_render_func, depth):")
1252        else:
1253            self.write(", undefined):" if extended_loop else ":")
1254
1255        self.indent()
1256        self.enter_frame(loop_frame)
1257
1258        self.writeline("_loop_vars = {}")
1259        self.blockvisit(node.body, loop_frame)
1260        if node.else_:
1261            self.writeline(f"{iteration_indicator} = 0")
1262        self.outdent()
1263        self.leave_frame(
1264            loop_frame, with_python_scope=node.recursive and not node.else_
1265        )
1266
1267        if node.else_:
1268            self.writeline(f"if {iteration_indicator}:")
1269            self.indent()
1270            self.enter_frame(else_frame)
1271            self.blockvisit(node.else_, else_frame)
1272            self.leave_frame(else_frame)
1273            self.outdent()
1274
1275        # if the node was recursive we have to return the buffer contents
1276        # and start the iteration code
1277        if node.recursive:
1278            self.return_buffer_contents(loop_frame)
1279            self.outdent()
1280            self.start_write(frame, node)
1281            self.write(f"{self.choose_async('await ')}loop(")
1282            if self.environment.is_async:
1283                self.write("auto_aiter(")
1284            self.visit(node.iter, frame)
1285            if self.environment.is_async:
1286                self.write(")")
1287            self.write(", loop)")
1288            self.end_write(frame)
1289
1290        # at the end of the iteration, clear any assignments made in the
1291        # loop from the top level
1292        if self._assign_stack:
1293            self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1294
1295    def visit_If(self, node: nodes.If, frame: Frame) -> None:
1296        if_frame = frame.soft()
1297        self.writeline("if ", node)
1298        self.visit(node.test, if_frame)
1299        self.write(":")
1300        self.indent()
1301        self.blockvisit(node.body, if_frame)
1302        self.outdent()
1303        for elif_ in node.elif_:
1304            self.writeline("elif ", elif_)
1305            self.visit(elif_.test, if_frame)
1306            self.write(":")
1307            self.indent()
1308            self.blockvisit(elif_.body, if_frame)
1309            self.outdent()
1310        if node.else_:
1311            self.writeline("else:")
1312            self.indent()
1313            self.blockvisit(node.else_, if_frame)
1314            self.outdent()
1315
1316    def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1317        macro_frame, macro_ref = self.macro_body(node, frame)
1318        self.newline()
1319        if frame.toplevel:
1320            if not node.name.startswith("_"):
1321                self.write(f"context.exported_vars.add({node.name!r})")
1322            self.writeline(f"context.vars[{node.name!r}] = ")
1323        self.write(f"{frame.symbols.ref(node.name)} = ")
1324        self.macro_def(macro_ref, macro_frame)
1325
1326    def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1327        call_frame, macro_ref = self.macro_body(node, frame)
1328        self.writeline("caller = ")
1329        self.macro_def(macro_ref, call_frame)
1330        self.start_write(frame, node)
1331        self.visit_Call(node.call, frame, forward_caller=True)
1332        self.end_write(frame)
1333
1334    def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1335        filter_frame = frame.inner()
1336        filter_frame.symbols.analyze_node(node)
1337        self.enter_frame(filter_frame)
1338        self.buffer(filter_frame)
1339        self.blockvisit(node.body, filter_frame)
1340        self.start_write(frame, node)
1341        self.visit_Filter(node.filter, filter_frame)
1342        self.end_write(frame)
1343        self.leave_frame(filter_frame)
1344
1345    def visit_With(self, node: nodes.With, frame: Frame) -> None:
1346        with_frame = frame.inner()
1347        with_frame.symbols.analyze_node(node)
1348        self.enter_frame(with_frame)
1349        for target, expr in zip(node.targets, node.values):
1350            self.newline()
1351            self.visit(target, with_frame)
1352            self.write(" = ")
1353            self.visit(expr, frame)
1354        self.blockvisit(node.body, with_frame)
1355        self.leave_frame(with_frame)
1356
1357    def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1358        self.newline(node)
1359        self.visit(node.node, frame)
1360
1361    class _FinalizeInfo(t.NamedTuple):
1362        const: t.Optional[t.Callable[..., str]]
1363        src: t.Optional[str]
1364
1365    @staticmethod
1366    def _default_finalize(value: t.Any) -> t.Any:
1367        """The default finalize function if the environment isn't
1368        configured with one. Or, if the environment has one, this is
1369        called on that function's output for constants.
1370        """
1371        return str(value)
1372
1373    _finalize: t.Optional[_FinalizeInfo] = None
1374
1375    def _make_finalize(self) -> _FinalizeInfo:
1376        """Build the finalize function to be used on constants and at
1377        runtime. Cached so it's only created once for all output nodes.
1378
1379        Returns a ``namedtuple`` with the following attributes:
1380
1381        ``const``
1382            A function to finalize constant data at compile time.
1383
1384        ``src``
1385            Source code to output around nodes to be evaluated at
1386            runtime.
1387        """
1388        if self._finalize is not None:
1389            return self._finalize
1390
1391        finalize: t.Optional[t.Callable[..., t.Any]]
1392        finalize = default = self._default_finalize
1393        src = None
1394
1395        if self.environment.finalize:
1396            src = "environment.finalize("
1397            env_finalize = self.environment.finalize
1398            pass_arg = {
1399                _PassArg.context: "context",
1400                _PassArg.eval_context: "context.eval_ctx",
1401                _PassArg.environment: "environment",
1402            }.get(
1403                _PassArg.from_obj(env_finalize)  # type: ignore
1404            )
1405            finalize = None
1406
1407            if pass_arg is None:
1408
1409                def finalize(value: t.Any) -> t.Any:  # noqa: F811
1410                    return default(env_finalize(value))
1411
1412            else:
1413                src = f"{src}{pass_arg}, "
1414
1415                if pass_arg == "environment":
1416
1417                    def finalize(value: t.Any) -> t.Any:  # noqa: F811
1418                        return default(env_finalize(self.environment, value))
1419
1420        self._finalize = self._FinalizeInfo(finalize, src)
1421        return self._finalize
1422
1423    def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
1424        """Given a group of constant values converted from ``Output``
1425        child nodes, produce a string to write to the template module
1426        source.
1427        """
1428        return repr(concat(group))
1429
1430    def _output_child_to_const(
1431        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1432    ) -> str:
1433        """Try to optimize a child of an ``Output`` node by trying to
1434        convert it to constant, finalized data at compile time.
1435
1436        If :exc:`Impossible` is raised, the node is not constant and
1437        will be evaluated at runtime. Any other exception will also be
1438        evaluated at runtime for easier debugging.
1439        """
1440        const = node.as_const(frame.eval_ctx)
1441
1442        if frame.eval_ctx.autoescape:
1443            const = escape(const)
1444
1445        # Template data doesn't go through finalize.
1446        if isinstance(node, nodes.TemplateData):
1447            return str(const)
1448
1449        return finalize.const(const)  # type: ignore
1450
1451    def _output_child_pre(
1452        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1453    ) -> None:
1454        """Output extra source code before visiting a child of an
1455        ``Output`` node.
1456        """
1457        if frame.eval_ctx.volatile:
1458            self.write("(escape if context.eval_ctx.autoescape else str)(")
1459        elif frame.eval_ctx.autoescape:
1460            self.write("escape(")
1461        else:
1462            self.write("str(")
1463
1464        if finalize.src is not None:
1465            self.write(finalize.src)
1466
1467    def _output_child_post(
1468        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1469    ) -> None:
1470        """Output extra source code after visiting a child of an
1471        ``Output`` node.
1472        """
1473        self.write(")")
1474
1475        if finalize.src is not None:
1476            self.write(")")
1477
1478    def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1479        # If an extends is active, don't render outside a block.
1480        if frame.require_output_check:
1481            # A top-level extends is known to exist at compile time.
1482            if self.has_known_extends:
1483                return
1484
1485            self.writeline("if parent_template is None:")
1486            self.indent()
1487
1488        finalize = self._make_finalize()
1489        body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1490
1491        # Evaluate constants at compile time if possible. Each item in
1492        # body will be either a list of static data or a node to be
1493        # evaluated at runtime.
1494        for child in node.nodes:
1495            try:
1496                if not (
1497                    # If the finalize function requires runtime context,
1498                    # constants can't be evaluated at compile time.
1499                    finalize.const
1500                    # Unless it's basic template data that won't be
1501                    # finalized anyway.
1502                    or isinstance(child, nodes.TemplateData)
1503                ):
1504                    raise nodes.Impossible()
1505
1506                const = self._output_child_to_const(child, frame, finalize)
1507            except (nodes.Impossible, Exception):
1508                # The node was not constant and needs to be evaluated at
1509                # runtime. Or another error was raised, which is easier
1510                # to debug at runtime.
1511                body.append(child)
1512                continue
1513
1514            if body and isinstance(body[-1], list):
1515                body[-1].append(const)
1516            else:
1517                body.append([const])
1518
1519        if frame.buffer is not None:
1520            if len(body) == 1:
1521                self.writeline(f"{frame.buffer}.append(")
1522            else:
1523                self.writeline(f"{frame.buffer}.extend((")
1524
1525            self.indent()
1526
1527        for item in body:
1528            if isinstance(item, list):
1529                # A group of constant data to join and output.
1530                val = self._output_const_repr(item)
1531
1532                if frame.buffer is None:
1533                    self.writeline("yield " + val)
1534                else:
1535                    self.writeline(val + ",")
1536            else:
1537                if frame.buffer is None:
1538                    self.writeline("yield ", item)
1539                else:
1540                    self.newline(item)
1541
1542                # A node to be evaluated at runtime.
1543                self._output_child_pre(item, frame, finalize)
1544                self.visit(item, frame)
1545                self._output_child_post(item, frame, finalize)
1546
1547                if frame.buffer is not None:
1548                    self.write(",")
1549
1550        if frame.buffer is not None:
1551            self.outdent()
1552            self.writeline(")" if len(body) == 1 else "))")
1553
1554        if frame.require_output_check:
1555            self.outdent()
1556
1557    def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1558        self.push_assign_tracking()
1559        self.newline(node)
1560        self.visit(node.target, frame)
1561        self.write(" = ")
1562        self.visit(node.node, frame)
1563        self.pop_assign_tracking(frame)
1564
1565    def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1566        self.push_assign_tracking()
1567        block_frame = frame.inner()
1568        # This is a special case.  Since a set block always captures we
1569        # will disable output checks.  This way one can use set blocks
1570        # toplevel even in extended templates.
1571        block_frame.require_output_check = False
1572        block_frame.symbols.analyze_node(node)
1573        self.enter_frame(block_frame)
1574        self.buffer(block_frame)
1575        self.blockvisit(node.body, block_frame)
1576        self.newline(node)
1577        self.visit(node.target, frame)
1578        self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1579        if node.filter is not None:
1580            self.visit_Filter(node.filter, block_frame)
1581        else:
1582            self.write(f"concat({block_frame.buffer})")
1583        self.write(")")
1584        self.pop_assign_tracking(frame)
1585        self.leave_frame(block_frame)
1586
1587    # -- Expression Visitors
1588
1589    def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1590        if node.ctx == "store" and (
1591            frame.toplevel or frame.loop_frame or frame.block_frame
1592        ):
1593            if self._assign_stack:
1594                self._assign_stack[-1].add(node.name)
1595        ref = frame.symbols.ref(node.name)
1596
1597        # If we are looking up a variable we might have to deal with the
1598        # case where it's undefined.  We can skip that case if the load
1599        # instruction indicates a parameter which are always defined.
1600        if node.ctx == "load":
1601            load = frame.symbols.find_load(ref)
1602            if not (
1603                load is not None
1604                and load[0] == VAR_LOAD_PARAMETER
1605                and not self.parameter_is_undeclared(ref)
1606            ):
1607                self.write(
1608                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1609                )
1610                return
1611
1612        self.write(ref)
1613
1614    def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1615        # NSRefs can only be used to store values; since they use the normal
1616        # `foo.bar` notation they will be parsed as a normal attribute access
1617        # when used anywhere but in a `set` context
1618        ref = frame.symbols.ref(node.name)
1619        self.writeline(f"if not isinstance({ref}, Namespace):")
1620        self.indent()
1621        self.writeline(
1622            "raise TemplateRuntimeError"
1623            '("cannot assign attribute on non-namespace object")'
1624        )
1625        self.outdent()
1626        self.writeline(f"{ref}[{node.attr!r}]")
1627
1628    def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1629        val = node.as_const(frame.eval_ctx)
1630        if isinstance(val, float):
1631            self.write(str(val))
1632        else:
1633            self.write(repr(val))
1634
1635    def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1636        try:
1637            self.write(repr(node.as_const(frame.eval_ctx)))
1638        except nodes.Impossible:
1639            self.write(
1640                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1641            )
1642
1643    def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1644        self.write("(")
1645        idx = -1
1646        for idx, item in enumerate(node.items):
1647            if idx:
1648                self.write(", ")
1649            self.visit(item, frame)
1650        self.write(",)" if idx == 0 else ")")
1651
1652    def visit_List(self, node: nodes.List, frame: Frame) -> None:
1653        self.write("[")
1654        for idx, item in enumerate(node.items):
1655            if idx:
1656                self.write(", ")
1657            self.visit(item, frame)
1658        self.write("]")
1659
1660    def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1661        self.write("{")
1662        for idx, item in enumerate(node.items):
1663            if idx:
1664                self.write(", ")
1665            self.visit(item.key, frame)
1666            self.write(": ")
1667            self.visit(item.value, frame)
1668        self.write("}")
1669
1670    visit_Add = _make_binop("+")
1671    visit_Sub = _make_binop("-")
1672    visit_Mul = _make_binop("*")
1673    visit_Div = _make_binop("/")
1674    visit_FloorDiv = _make_binop("//")
1675    visit_Pow = _make_binop("**")
1676    visit_Mod = _make_binop("%")
1677    visit_And = _make_binop("and")
1678    visit_Or = _make_binop("or")
1679    visit_Pos = _make_unop("+")
1680    visit_Neg = _make_unop("-")
1681    visit_Not = _make_unop("not ")
1682
1683    @optimizeconst
1684    def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1685        if frame.eval_ctx.volatile:
1686            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1687        elif frame.eval_ctx.autoescape:
1688            func_name = "markup_join"
1689        else:
1690            func_name = "str_join"
1691        self.write(f"{func_name}((")
1692        for arg in node.nodes:
1693            self.visit(arg, frame)
1694            self.write(", ")
1695        self.write("))")
1696
1697    @optimizeconst
1698    def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1699        self.write("(")
1700        self.visit(node.expr, frame)
1701        for op in node.ops:
1702            self.visit(op, frame)
1703        self.write(")")
1704
1705    def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1706        self.write(f" {operators[node.op]} ")
1707        self.visit(node.expr, frame)
1708
1709    @optimizeconst
1710    def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1711        if self.environment.is_async:
1712            self.write("(await auto_await(")
1713
1714        self.write("environment.getattr(")
1715        self.visit(node.node, frame)
1716        self.write(f", {node.attr!r})")
1717
1718        if self.environment.is_async:
1719            self.write("))")
1720
1721    @optimizeconst
1722    def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1723        # slices bypass the environment getitem method.
1724        if isinstance(node.arg, nodes.Slice):
1725            self.visit(node.node, frame)
1726            self.write("[")
1727            self.visit(node.arg, frame)
1728            self.write("]")
1729        else:
1730            if self.environment.is_async:
1731                self.write("(await auto_await(")
1732
1733            self.write("environment.getitem(")
1734            self.visit(node.node, frame)
1735            self.write(", ")
1736            self.visit(node.arg, frame)
1737            self.write(")")
1738
1739            if self.environment.is_async:
1740                self.write("))")
1741
1742    def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1743        if node.start is not None:
1744            self.visit(node.start, frame)
1745        self.write(":")
1746        if node.stop is not None:
1747            self.visit(node.stop, frame)
1748        if node.step is not None:
1749            self.write(":")
1750            self.visit(node.step, frame)
1751
1752    @contextmanager
1753    def _filter_test_common(
1754        self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
1755    ) -> t.Iterator[None]:
1756        if self.environment.is_async:
1757            self.write("(await auto_await(")
1758
1759        if is_filter:
1760            self.write(f"{self.filters[node.name]}(")
1761            func = self.environment.filters.get(node.name)
1762        else:
1763            self.write(f"{self.tests[node.name]}(")
1764            func = self.environment.tests.get(node.name)
1765
1766        # When inside an If or CondExpr frame, allow the filter to be
1767        # undefined at compile time and only raise an error if it's
1768        # actually called at runtime. See pull_dependencies.
1769        if func is None and not frame.soft_frame:
1770            type_name = "filter" if is_filter else "test"
1771            self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
1772
1773        pass_arg = {
1774            _PassArg.context: "context",
1775            _PassArg.eval_context: "context.eval_ctx",
1776            _PassArg.environment: "environment",
1777        }.get(
1778            _PassArg.from_obj(func)  # type: ignore
1779        )
1780
1781        if pass_arg is not None:
1782            self.write(f"{pass_arg}, ")
1783
1784        # Back to the visitor function to handle visiting the target of
1785        # the filter or test.
1786        yield
1787
1788        self.signature(node, frame)
1789        self.write(")")
1790
1791        if self.environment.is_async:
1792            self.write("))")
1793
1794    @optimizeconst
1795    def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1796        with self._filter_test_common(node, frame, True):
1797            # if the filter node is None we are inside a filter block
1798            # and want to write to the current buffer
1799            if node.node is not None:
1800                self.visit(node.node, frame)
1801            elif frame.eval_ctx.volatile:
1802                self.write(
1803                    f"(Markup(concat({frame.buffer}))"
1804                    f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1805                )
1806            elif frame.eval_ctx.autoescape:
1807                self.write(f"Markup(concat({frame.buffer}))")
1808            else:
1809                self.write(f"concat({frame.buffer})")
1810
1811    @optimizeconst
1812    def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1813        with self._filter_test_common(node, frame, False):
1814            self.visit(node.node, frame)
1815
1816    @optimizeconst
1817    def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1818        frame = frame.soft()
1819
1820        def write_expr2() -> None:
1821            if node.expr2 is not None:
1822                self.visit(node.expr2, frame)
1823                return
1824
1825            self.write(
1826                f'cond_expr_undefined("the inline if-expression on'
1827                f" {self.position(node)} evaluated to false and no else"
1828                f' section was defined.")'
1829            )
1830
1831        self.write("(")
1832        self.visit(node.expr1, frame)
1833        self.write(" if ")
1834        self.visit(node.test, frame)
1835        self.write(" else ")
1836        write_expr2()
1837        self.write(")")
1838
1839    @optimizeconst
1840    def visit_Call(
1841        self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1842    ) -> None:
1843        if self.environment.is_async:
1844            self.write("(await auto_await(")
1845        if self.environment.sandboxed:
1846            self.write("environment.call(context, ")
1847        else:
1848            self.write("context.call(")
1849        self.visit(node.node, frame)
1850        extra_kwargs = {"caller": "caller"} if forward_caller else None
1851        loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1852        block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1853        if extra_kwargs:
1854            extra_kwargs.update(loop_kwargs, **block_kwargs)
1855        elif loop_kwargs or block_kwargs:
1856            extra_kwargs = dict(loop_kwargs, **block_kwargs)
1857        self.signature(node, frame, extra_kwargs)
1858        self.write(")")
1859        if self.environment.is_async:
1860            self.write("))")
1861
1862    def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1863        self.write(node.key + "=")
1864        self.visit(node.value, frame)
1865
1866    # -- Unused nodes for extensions
1867
1868    def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1869        self.write("Markup(")
1870        self.visit(node.expr, frame)
1871        self.write(")")
1872
1873    def visit_MarkSafeIfAutoescape(
1874        self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1875    ) -> None:
1876        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1877        self.visit(node.expr, frame)
1878        self.write(")")
1879
1880    def visit_EnvironmentAttribute(
1881        self, node: nodes.EnvironmentAttribute, frame: Frame
1882    ) -> None:
1883        self.write("environment." + node.name)
1884
1885    def visit_ExtensionAttribute(
1886        self, node: nodes.ExtensionAttribute, frame: Frame
1887    ) -> None:
1888        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
1889
1890    def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1891        self.write(self.import_aliases[node.importname])
1892
1893    def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1894        self.write(node.name)
1895
1896    def visit_ContextReference(
1897        self, node: nodes.ContextReference, frame: Frame
1898    ) -> None:
1899        self.write("context")
1900
1901    def visit_DerivedContextReference(
1902        self, node: nodes.DerivedContextReference, frame: Frame
1903    ) -> None:
1904        self.write(self.derive_context(frame))
1905
1906    def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1907        self.writeline("continue", node)
1908
1909    def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1910        self.writeline("break", node)
1911
1912    def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1913        scope_frame = frame.inner()
1914        scope_frame.symbols.analyze_node(node)
1915        self.enter_frame(scope_frame)
1916        self.blockvisit(node.body, scope_frame)
1917        self.leave_frame(scope_frame)
1918
1919    def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1920        ctx = self.temporary_identifier()
1921        self.writeline(f"{ctx} = {self.derive_context(frame)}")
1922        self.writeline(f"{ctx}.vars = ")
1923        self.visit(node.context, frame)
1924        self.push_context_reference(ctx)
1925
1926        scope_frame = frame.inner(isolated=True)
1927        scope_frame.symbols.analyze_node(node)
1928        self.enter_frame(scope_frame)
1929        self.blockvisit(node.body, scope_frame)
1930        self.leave_frame(scope_frame)
1931        self.pop_context_reference()
1932
1933    def visit_EvalContextModifier(
1934        self, node: nodes.EvalContextModifier, frame: Frame
1935    ) -> None:
1936        for keyword in node.options:
1937            self.writeline(f"context.eval_ctx.{keyword.key} = ")
1938            self.visit(keyword.value, frame)
1939            try:
1940                val = keyword.value.as_const(frame.eval_ctx)
1941            except nodes.Impossible:
1942                frame.eval_ctx.volatile = True
1943            else:
1944                setattr(frame.eval_ctx, keyword.key, val)
1945
1946    def visit_ScopedEvalContextModifier(
1947        self, node: nodes.ScopedEvalContextModifier, frame: Frame
1948    ) -> None:
1949        old_ctx_name = self.temporary_identifier()
1950        saved_ctx = frame.eval_ctx.save()
1951        self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1952        self.visit_EvalContextModifier(node, frame)
1953        for child in node.body:
1954            self.visit(child, frame)
1955        frame.eval_ctx.revert(saved_ctx)
1956        self.writeline(f"context.eval_ctx.revert({old_ctx_name})")
operators = {'eq': '==', 'ne': '!=', 'gt': '>', 'gteq': '>=', 'lt': '<', 'lteq': '<=', 'in': 'in', 'notin': 'not in'}
def optimizeconst(f: ~F) -> ~F:
44def optimizeconst(f: F) -> F:
45    def new_func(
46        self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
47    ) -> t.Any:
48        # Only optimize if the frame is not volatile
49        if self.optimizer is not None and not frame.eval_ctx.volatile:
50            new_node = self.optimizer.visit(node, frame.eval_ctx)
51
52            if new_node != node:
53                return self.visit(new_node, frame)
54
55        return f(self, node, frame, **kwargs)
56
57    return update_wrapper(t.cast(F, new_func), f)
def generate( node: jinja2.nodes.Template, environment: jinja2.environment.Environment, name: Optional[str], filename: Optional[str], stream: Optional[TextIO] = None, defer_init: bool = False, optimized: bool = True) -> Optional[str]:
102def generate(
103    node: nodes.Template,
104    environment: "Environment",
105    name: t.Optional[str],
106    filename: t.Optional[str],
107    stream: t.Optional[t.TextIO] = None,
108    defer_init: bool = False,
109    optimized: bool = True,
110) -> t.Optional[str]:
111    """Generate the python source for a node tree."""
112    if not isinstance(node, nodes.Template):
113        raise TypeError("Can't compile non template nodes")
114
115    generator = environment.code_generator_class(
116        environment, name, filename, stream, defer_init, optimized
117    )
118    generator.visit(node)
119
120    if stream is None:
121        return generator.stream.getvalue()  # type: ignore
122
123    return None

Generate the python source for a node tree.

def has_safe_repr(value: Any) -> bool:
126def has_safe_repr(value: t.Any) -> bool:
127    """Does the node have a safe representation?"""
128    if value is None or value is NotImplemented or value is Ellipsis:
129        return True
130
131    if type(value) in {bool, int, float, complex, range, str, Markup}:
132        return True
133
134    if type(value) in {tuple, list, set, frozenset}:
135        return all(has_safe_repr(v) for v in value)
136
137    if type(value) is dict:
138        return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
139
140    return False

Does the node have a safe representation?

def find_undeclared(nodes: Iterable[jinja2.nodes.Node], names: Iterable[str]) -> Set[str]:
143def find_undeclared(
144    nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
145) -> t.Set[str]:
146    """Check if the names passed are accessed undeclared.  The return value
147    is a set of all the undeclared names from the sequence of names found.
148    """
149    visitor = UndeclaredNameVisitor(names)
150    try:
151        for node in nodes:
152            visitor.visit(node)
153    except VisitorExit:
154        pass
155    return visitor.undeclared

Check if the names passed are accessed undeclared. The return value is a set of all the undeclared names from the sequence of names found.

class MacroRef:
158class MacroRef:
159    def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
160        self.node = node
161        self.accesses_caller = False
162        self.accesses_kwargs = False
163        self.accesses_varargs = False
MacroRef(node: Union[jinja2.nodes.Macro, jinja2.nodes.CallBlock])
159    def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
160        self.node = node
161        self.accesses_caller = False
162        self.accesses_kwargs = False
163        self.accesses_varargs = False
node
accesses_caller
accesses_kwargs
accesses_varargs
class Frame:
166class Frame:
167    """Holds compile time information for us."""
168
169    def __init__(
170        self,
171        eval_ctx: EvalContext,
172        parent: t.Optional["Frame"] = None,
173        level: t.Optional[int] = None,
174    ) -> None:
175        self.eval_ctx = eval_ctx
176
177        # the parent of this frame
178        self.parent = parent
179
180        if parent is None:
181            self.symbols = Symbols(level=level)
182
183            # in some dynamic inheritance situations the compiler needs to add
184            # write tests around output statements.
185            self.require_output_check = False
186
187            # inside some tags we are using a buffer rather than yield statements.
188            # this for example affects {% filter %} or {% macro %}.  If a frame
189            # is buffered this variable points to the name of the list used as
190            # buffer.
191            self.buffer: t.Optional[str] = None
192
193            # the name of the block we're in, otherwise None.
194            self.block: t.Optional[str] = None
195
196        else:
197            self.symbols = Symbols(parent.symbols, level=level)
198            self.require_output_check = parent.require_output_check
199            self.buffer = parent.buffer
200            self.block = parent.block
201
202        # a toplevel frame is the root + soft frames such as if conditions.
203        self.toplevel = False
204
205        # the root frame is basically just the outermost frame, so no if
206        # conditions.  This information is used to optimize inheritance
207        # situations.
208        self.rootlevel = False
209
210        # variables set inside of loops and blocks should not affect outer frames,
211        # but they still needs to be kept track of as part of the active context.
212        self.loop_frame = False
213        self.block_frame = False
214
215        # track whether the frame is being used in an if-statement or conditional
216        # expression as it determines which errors should be raised during runtime
217        # or compile time.
218        self.soft_frame = False
219
220    def copy(self) -> "Frame":
221        """Create a copy of the current one."""
222        rv = object.__new__(self.__class__)
223        rv.__dict__.update(self.__dict__)
224        rv.symbols = self.symbols.copy()
225        return rv
226
227    def inner(self, isolated: bool = False) -> "Frame":
228        """Return an inner frame."""
229        if isolated:
230            return Frame(self.eval_ctx, level=self.symbols.level + 1)
231        return Frame(self.eval_ctx, self)
232
233    def soft(self) -> "Frame":
234        """Return a soft frame.  A soft frame may not be modified as
235        standalone thing as it shares the resources with the frame it
236        was created of, but it's not a rootlevel frame any longer.
237
238        This is only used to implement if-statements and conditional
239        expressions.
240        """
241        rv = self.copy()
242        rv.rootlevel = False
243        rv.soft_frame = True
244        return rv
245
246    __copy__ = copy

Holds compile time information for us.

Frame( eval_ctx: jinja2.nodes.EvalContext, parent: Optional[Frame] = None, level: Optional[int] = None)
169    def __init__(
170        self,
171        eval_ctx: EvalContext,
172        parent: t.Optional["Frame"] = None,
173        level: t.Optional[int] = None,
174    ) -> None:
175        self.eval_ctx = eval_ctx
176
177        # the parent of this frame
178        self.parent = parent
179
180        if parent is None:
181            self.symbols = Symbols(level=level)
182
183            # in some dynamic inheritance situations the compiler needs to add
184            # write tests around output statements.
185            self.require_output_check = False
186
187            # inside some tags we are using a buffer rather than yield statements.
188            # this for example affects {% filter %} or {% macro %}.  If a frame
189            # is buffered this variable points to the name of the list used as
190            # buffer.
191            self.buffer: t.Optional[str] = None
192
193            # the name of the block we're in, otherwise None.
194            self.block: t.Optional[str] = None
195
196        else:
197            self.symbols = Symbols(parent.symbols, level=level)
198            self.require_output_check = parent.require_output_check
199            self.buffer = parent.buffer
200            self.block = parent.block
201
202        # a toplevel frame is the root + soft frames such as if conditions.
203        self.toplevel = False
204
205        # the root frame is basically just the outermost frame, so no if
206        # conditions.  This information is used to optimize inheritance
207        # situations.
208        self.rootlevel = False
209
210        # variables set inside of loops and blocks should not affect outer frames,
211        # but they still needs to be kept track of as part of the active context.
212        self.loop_frame = False
213        self.block_frame = False
214
215        # track whether the frame is being used in an if-statement or conditional
216        # expression as it determines which errors should be raised during runtime
217        # or compile time.
218        self.soft_frame = False
eval_ctx
parent
toplevel
rootlevel
loop_frame
block_frame
soft_frame
def copy(self) -> Frame:
220    def copy(self) -> "Frame":
221        """Create a copy of the current one."""
222        rv = object.__new__(self.__class__)
223        rv.__dict__.update(self.__dict__)
224        rv.symbols = self.symbols.copy()
225        return rv

Create a copy of the current one.

def inner(self, isolated: bool = False) -> Frame:
227    def inner(self, isolated: bool = False) -> "Frame":
228        """Return an inner frame."""
229        if isolated:
230            return Frame(self.eval_ctx, level=self.symbols.level + 1)
231        return Frame(self.eval_ctx, self)

Return an inner frame.

def soft(self) -> Frame:
233    def soft(self) -> "Frame":
234        """Return a soft frame.  A soft frame may not be modified as
235        standalone thing as it shares the resources with the frame it
236        was created of, but it's not a rootlevel frame any longer.
237
238        This is only used to implement if-statements and conditional
239        expressions.
240        """
241        rv = self.copy()
242        rv.rootlevel = False
243        rv.soft_frame = True
244        return rv

Return a soft frame. A soft frame may not be modified as standalone thing as it shares the resources with the frame it was created of, but it's not a rootlevel frame any longer.

This is only used to implement if-statements and conditional expressions.

class VisitorExit(builtins.RuntimeError):
249class VisitorExit(RuntimeError):
250    """Exception used by the `UndeclaredNameVisitor` to signal a stop."""

Exception used by the UndeclaredNameVisitor to signal a stop.

Inherited Members
builtins.RuntimeError
RuntimeError
builtins.BaseException
with_traceback
add_note
args
class DependencyFinderVisitor(jinja2.visitor.NodeVisitor):
253class DependencyFinderVisitor(NodeVisitor):
254    """A visitor that collects filter and test calls."""
255
256    def __init__(self) -> None:
257        self.filters: t.Set[str] = set()
258        self.tests: t.Set[str] = set()
259
260    def visit_Filter(self, node: nodes.Filter) -> None:
261        self.generic_visit(node)
262        self.filters.add(node.name)
263
264    def visit_Test(self, node: nodes.Test) -> None:
265        self.generic_visit(node)
266        self.tests.add(node.name)
267
268    def visit_Block(self, node: nodes.Block) -> None:
269        """Stop visiting at blocks."""

A visitor that collects filter and test calls.

filters: Set[str]
tests: Set[str]
def visit_Filter(self, node: jinja2.nodes.Filter) -> None:
260    def visit_Filter(self, node: nodes.Filter) -> None:
261        self.generic_visit(node)
262        self.filters.add(node.name)
def visit_Test(self, node: jinja2.nodes.Test) -> None:
264    def visit_Test(self, node: nodes.Test) -> None:
265        self.generic_visit(node)
266        self.tests.add(node.name)
def visit_Block(self, node: jinja2.nodes.Block) -> None:
268    def visit_Block(self, node: nodes.Block) -> None:
269        """Stop visiting at blocks."""

Stop visiting at blocks.

class UndeclaredNameVisitor(jinja2.visitor.NodeVisitor):
272class UndeclaredNameVisitor(NodeVisitor):
273    """A visitor that checks if a name is accessed without being
274    declared.  This is different from the frame visitor as it will
275    not stop at closure frames.
276    """
277
278    def __init__(self, names: t.Iterable[str]) -> None:
279        self.names = set(names)
280        self.undeclared: t.Set[str] = set()
281
282    def visit_Name(self, node: nodes.Name) -> None:
283        if node.ctx == "load" and node.name in self.names:
284            self.undeclared.add(node.name)
285            if self.undeclared == self.names:
286                raise VisitorExit()
287        else:
288            self.names.discard(node.name)
289
290    def visit_Block(self, node: nodes.Block) -> None:
291        """Stop visiting a blocks."""

A visitor that checks if a name is accessed without being declared. This is different from the frame visitor as it will not stop at closure frames.

UndeclaredNameVisitor(names: Iterable[str])
278    def __init__(self, names: t.Iterable[str]) -> None:
279        self.names = set(names)
280        self.undeclared: t.Set[str] = set()
names
undeclared: Set[str]
def visit_Name(self, node: jinja2.nodes.Name) -> None:
282    def visit_Name(self, node: nodes.Name) -> None:
283        if node.ctx == "load" and node.name in self.names:
284            self.undeclared.add(node.name)
285            if self.undeclared == self.names:
286                raise VisitorExit()
287        else:
288            self.names.discard(node.name)
def visit_Block(self, node: jinja2.nodes.Block) -> None:
290    def visit_Block(self, node: nodes.Block) -> None:
291        """Stop visiting a blocks."""

Stop visiting a blocks.

class CompilerExit(builtins.Exception):
294class CompilerExit(Exception):
295    """Raised if the compiler encountered a situation where it just
296    doesn't make sense to further process the code.  Any block that
297    raises such an exception is not further processed.
298    """

Raised if the compiler encountered a situation where it just doesn't make sense to further process the code. Any block that raises such an exception is not further processed.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
add_note
args
class CodeGenerator(jinja2.visitor.NodeVisitor):
 301class CodeGenerator(NodeVisitor):
 302    def __init__(
 303        self,
 304        environment: "Environment",
 305        name: t.Optional[str],
 306        filename: t.Optional[str],
 307        stream: t.Optional[t.TextIO] = None,
 308        defer_init: bool = False,
 309        optimized: bool = True,
 310    ) -> None:
 311        if stream is None:
 312            stream = StringIO()
 313        self.environment = environment
 314        self.name = name
 315        self.filename = filename
 316        self.stream = stream
 317        self.created_block_context = False
 318        self.defer_init = defer_init
 319        self.optimizer: t.Optional[Optimizer] = None
 320
 321        if optimized:
 322            self.optimizer = Optimizer(environment)
 323
 324        # aliases for imports
 325        self.import_aliases: t.Dict[str, str] = {}
 326
 327        # a registry for all blocks.  Because blocks are moved out
 328        # into the global python scope they are registered here
 329        self.blocks: t.Dict[str, nodes.Block] = {}
 330
 331        # the number of extends statements so far
 332        self.extends_so_far = 0
 333
 334        # some templates have a rootlevel extends.  In this case we
 335        # can safely assume that we're a child template and do some
 336        # more optimizations.
 337        self.has_known_extends = False
 338
 339        # the current line number
 340        self.code_lineno = 1
 341
 342        # registry of all filters and tests (global, not block local)
 343        self.tests: t.Dict[str, str] = {}
 344        self.filters: t.Dict[str, str] = {}
 345
 346        # the debug information
 347        self.debug_info: t.List[t.Tuple[int, int]] = []
 348        self._write_debug_info: t.Optional[int] = None
 349
 350        # the number of new lines before the next write()
 351        self._new_lines = 0
 352
 353        # the line number of the last written statement
 354        self._last_line = 0
 355
 356        # true if nothing was written so far.
 357        self._first_write = True
 358
 359        # used by the `temporary_identifier` method to get new
 360        # unique, temporary identifier
 361        self._last_identifier = 0
 362
 363        # the current indentation
 364        self._indentation = 0
 365
 366        # Tracks toplevel assignments
 367        self._assign_stack: t.List[t.Set[str]] = []
 368
 369        # Tracks parameter definition blocks
 370        self._param_def_block: t.List[t.Set[str]] = []
 371
 372        # Tracks the current context.
 373        self._context_reference_stack = ["context"]
 374
 375    @property
 376    def optimized(self) -> bool:
 377        return self.optimizer is not None
 378
 379    # -- Various compilation helpers
 380
 381    def fail(self, msg: str, lineno: int) -> "te.NoReturn":
 382        """Fail with a :exc:`TemplateAssertionError`."""
 383        raise TemplateAssertionError(msg, lineno, self.name, self.filename)
 384
 385    def temporary_identifier(self) -> str:
 386        """Get a new unique identifier."""
 387        self._last_identifier += 1
 388        return f"t_{self._last_identifier}"
 389
 390    def buffer(self, frame: Frame) -> None:
 391        """Enable buffering for the frame from that point onwards."""
 392        frame.buffer = self.temporary_identifier()
 393        self.writeline(f"{frame.buffer} = []")
 394
 395    def return_buffer_contents(
 396        self, frame: Frame, force_unescaped: bool = False
 397    ) -> None:
 398        """Return the buffer contents of the frame."""
 399        if not force_unescaped:
 400            if frame.eval_ctx.volatile:
 401                self.writeline("if context.eval_ctx.autoescape:")
 402                self.indent()
 403                self.writeline(f"return Markup(concat({frame.buffer}))")
 404                self.outdent()
 405                self.writeline("else:")
 406                self.indent()
 407                self.writeline(f"return concat({frame.buffer})")
 408                self.outdent()
 409                return
 410            elif frame.eval_ctx.autoescape:
 411                self.writeline(f"return Markup(concat({frame.buffer}))")
 412                return
 413        self.writeline(f"return concat({frame.buffer})")
 414
 415    def indent(self) -> None:
 416        """Indent by one."""
 417        self._indentation += 1
 418
 419    def outdent(self, step: int = 1) -> None:
 420        """Outdent by step."""
 421        self._indentation -= step
 422
 423    def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
 424        """Yield or write into the frame buffer."""
 425        if frame.buffer is None:
 426            self.writeline("yield ", node)
 427        else:
 428            self.writeline(f"{frame.buffer}.append(", node)
 429
 430    def end_write(self, frame: Frame) -> None:
 431        """End the writing process started by `start_write`."""
 432        if frame.buffer is not None:
 433            self.write(")")
 434
 435    def simple_write(
 436        self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
 437    ) -> None:
 438        """Simple shortcut for start_write + write + end_write."""
 439        self.start_write(frame, node)
 440        self.write(s)
 441        self.end_write(frame)
 442
 443    def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
 444        """Visit a list of nodes as block in a frame.  If the current frame
 445        is no buffer a dummy ``if 0: yield None`` is written automatically.
 446        """
 447        try:
 448            self.writeline("pass")
 449            for node in nodes:
 450                self.visit(node, frame)
 451        except CompilerExit:
 452            pass
 453
 454    def write(self, x: str) -> None:
 455        """Write a string into the output stream."""
 456        if self._new_lines:
 457            if not self._first_write:
 458                self.stream.write("\n" * self._new_lines)
 459                self.code_lineno += self._new_lines
 460                if self._write_debug_info is not None:
 461                    self.debug_info.append((self._write_debug_info, self.code_lineno))
 462                    self._write_debug_info = None
 463            self._first_write = False
 464            self.stream.write("    " * self._indentation)
 465            self._new_lines = 0
 466        self.stream.write(x)
 467
 468    def writeline(
 469        self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
 470    ) -> None:
 471        """Combination of newline and write."""
 472        self.newline(node, extra)
 473        self.write(x)
 474
 475    def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
 476        """Add one or more newlines before the next write."""
 477        self._new_lines = max(self._new_lines, 1 + extra)
 478        if node is not None and node.lineno != self._last_line:
 479            self._write_debug_info = node.lineno
 480            self._last_line = node.lineno
 481
 482    def signature(
 483        self,
 484        node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
 485        frame: Frame,
 486        extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
 487    ) -> None:
 488        """Writes a function call to the stream for the current node.
 489        A leading comma is added automatically.  The extra keyword
 490        arguments may not include python keywords otherwise a syntax
 491        error could occur.  The extra keyword arguments should be given
 492        as python dict.
 493        """
 494        # if any of the given keyword arguments is a python keyword
 495        # we have to make sure that no invalid call is created.
 496        kwarg_workaround = any(
 497            is_python_keyword(t.cast(str, k))
 498            for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
 499        )
 500
 501        for arg in node.args:
 502            self.write(", ")
 503            self.visit(arg, frame)
 504
 505        if not kwarg_workaround:
 506            for kwarg in node.kwargs:
 507                self.write(", ")
 508                self.visit(kwarg, frame)
 509            if extra_kwargs is not None:
 510                for key, value in extra_kwargs.items():
 511                    self.write(f", {key}={value}")
 512        if node.dyn_args:
 513            self.write(", *")
 514            self.visit(node.dyn_args, frame)
 515
 516        if kwarg_workaround:
 517            if node.dyn_kwargs is not None:
 518                self.write(", **dict({")
 519            else:
 520                self.write(", **{")
 521            for kwarg in node.kwargs:
 522                self.write(f"{kwarg.key!r}: ")
 523                self.visit(kwarg.value, frame)
 524                self.write(", ")
 525            if extra_kwargs is not None:
 526                for key, value in extra_kwargs.items():
 527                    self.write(f"{key!r}: {value}, ")
 528            if node.dyn_kwargs is not None:
 529                self.write("}, **")
 530                self.visit(node.dyn_kwargs, frame)
 531                self.write(")")
 532            else:
 533                self.write("}")
 534
 535        elif node.dyn_kwargs is not None:
 536            self.write(", **")
 537            self.visit(node.dyn_kwargs, frame)
 538
 539    def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
 540        """Find all filter and test names used in the template and
 541        assign them to variables in the compiled namespace. Checking
 542        that the names are registered with the environment is done when
 543        compiling the Filter and Test nodes. If the node is in an If or
 544        CondExpr node, the check is done at runtime instead.
 545
 546        .. versionchanged:: 3.0
 547            Filters and tests in If and CondExpr nodes are checked at
 548            runtime instead of compile time.
 549        """
 550        visitor = DependencyFinderVisitor()
 551
 552        for node in nodes:
 553            visitor.visit(node)
 554
 555        for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
 556            self.tests,
 557            visitor.tests,
 558            "tests",
 559        ):
 560            for name in sorted(names):
 561                if name not in id_map:
 562                    id_map[name] = self.temporary_identifier()
 563
 564                # add check during runtime that dependencies used inside of executed
 565                # blocks are defined, as this step may be skipped during compile time
 566                self.writeline("try:")
 567                self.indent()
 568                self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
 569                self.outdent()
 570                self.writeline("except KeyError:")
 571                self.indent()
 572                self.writeline("@internalcode")
 573                self.writeline(f"def {id_map[name]}(*unused):")
 574                self.indent()
 575                self.writeline(
 576                    f'raise TemplateRuntimeError("No {dependency[:-1]}'
 577                    f' named {name!r} found.")'
 578                )
 579                self.outdent()
 580                self.outdent()
 581
 582    def enter_frame(self, frame: Frame) -> None:
 583        undefs = []
 584        for target, (action, param) in frame.symbols.loads.items():
 585            if action == VAR_LOAD_PARAMETER:
 586                pass
 587            elif action == VAR_LOAD_RESOLVE:
 588                self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
 589            elif action == VAR_LOAD_ALIAS:
 590                self.writeline(f"{target} = {param}")
 591            elif action == VAR_LOAD_UNDEFINED:
 592                undefs.append(target)
 593            else:
 594                raise NotImplementedError("unknown load instruction")
 595        if undefs:
 596            self.writeline(f"{' = '.join(undefs)} = missing")
 597
 598    def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
 599        if not with_python_scope:
 600            undefs = []
 601            for target in frame.symbols.loads:
 602                undefs.append(target)
 603            if undefs:
 604                self.writeline(f"{' = '.join(undefs)} = missing")
 605
 606    def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
 607        return async_value if self.environment.is_async else sync_value
 608
 609    def func(self, name: str) -> str:
 610        return f"{self.choose_async()}def {name}"
 611
 612    def macro_body(
 613        self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
 614    ) -> t.Tuple[Frame, MacroRef]:
 615        """Dump the function def of a macro or call block."""
 616        frame = frame.inner()
 617        frame.symbols.analyze_node(node)
 618        macro_ref = MacroRef(node)
 619
 620        explicit_caller = None
 621        skip_special_params = set()
 622        args = []
 623
 624        for idx, arg in enumerate(node.args):
 625            if arg.name == "caller":
 626                explicit_caller = idx
 627            if arg.name in ("kwargs", "varargs"):
 628                skip_special_params.add(arg.name)
 629            args.append(frame.symbols.ref(arg.name))
 630
 631        undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
 632
 633        if "caller" in undeclared:
 634            # In older Jinja versions there was a bug that allowed caller
 635            # to retain the special behavior even if it was mentioned in
 636            # the argument list.  However thankfully this was only really
 637            # working if it was the last argument.  So we are explicitly
 638            # checking this now and error out if it is anywhere else in
 639            # the argument list.
 640            if explicit_caller is not None:
 641                try:
 642                    node.defaults[explicit_caller - len(node.args)]
 643                except IndexError:
 644                    self.fail(
 645                        "When defining macros or call blocks the "
 646                        'special "caller" argument must be omitted '
 647                        "or be given a default.",
 648                        node.lineno,
 649                    )
 650            else:
 651                args.append(frame.symbols.declare_parameter("caller"))
 652            macro_ref.accesses_caller = True
 653        if "kwargs" in undeclared and "kwargs" not in skip_special_params:
 654            args.append(frame.symbols.declare_parameter("kwargs"))
 655            macro_ref.accesses_kwargs = True
 656        if "varargs" in undeclared and "varargs" not in skip_special_params:
 657            args.append(frame.symbols.declare_parameter("varargs"))
 658            macro_ref.accesses_varargs = True
 659
 660        # macros are delayed, they never require output checks
 661        frame.require_output_check = False
 662        frame.symbols.analyze_node(node)
 663        self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
 664        self.indent()
 665
 666        self.buffer(frame)
 667        self.enter_frame(frame)
 668
 669        self.push_parameter_definitions(frame)
 670        for idx, arg in enumerate(node.args):
 671            ref = frame.symbols.ref(arg.name)
 672            self.writeline(f"if {ref} is missing:")
 673            self.indent()
 674            try:
 675                default = node.defaults[idx - len(node.args)]
 676            except IndexError:
 677                self.writeline(
 678                    f'{ref} = undefined("parameter {arg.name!r} was not provided",'
 679                    f" name={arg.name!r})"
 680                )
 681            else:
 682                self.writeline(f"{ref} = ")
 683                self.visit(default, frame)
 684            self.mark_parameter_stored(ref)
 685            self.outdent()
 686        self.pop_parameter_definitions()
 687
 688        self.blockvisit(node.body, frame)
 689        self.return_buffer_contents(frame, force_unescaped=True)
 690        self.leave_frame(frame, with_python_scope=True)
 691        self.outdent()
 692
 693        return frame, macro_ref
 694
 695    def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
 696        """Dump the macro definition for the def created by macro_body."""
 697        arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
 698        name = getattr(macro_ref.node, "name", None)
 699        if len(macro_ref.node.args) == 1:
 700            arg_tuple += ","
 701        self.write(
 702            f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
 703            f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
 704            f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
 705        )
 706
 707    def position(self, node: nodes.Node) -> str:
 708        """Return a human readable position for the node."""
 709        rv = f"line {node.lineno}"
 710        if self.name is not None:
 711            rv = f"{rv} in {self.name!r}"
 712        return rv
 713
 714    def dump_local_context(self, frame: Frame) -> str:
 715        items_kv = ", ".join(
 716            f"{name!r}: {target}"
 717            for name, target in frame.symbols.dump_stores().items()
 718        )
 719        return f"{{{items_kv}}}"
 720
 721    def write_commons(self) -> None:
 722        """Writes a common preamble that is used by root and block functions.
 723        Primarily this sets up common local helpers and enforces a generator
 724        through a dead branch.
 725        """
 726        self.writeline("resolve = context.resolve_or_missing")
 727        self.writeline("undefined = environment.undefined")
 728        self.writeline("concat = environment.concat")
 729        # always use the standard Undefined class for the implicit else of
 730        # conditional expressions
 731        self.writeline("cond_expr_undefined = Undefined")
 732        self.writeline("if 0: yield None")
 733
 734    def push_parameter_definitions(self, frame: Frame) -> None:
 735        """Pushes all parameter targets from the given frame into a local
 736        stack that permits tracking of yet to be assigned parameters.  In
 737        particular this enables the optimization from `visit_Name` to skip
 738        undefined expressions for parameters in macros as macros can reference
 739        otherwise unbound parameters.
 740        """
 741        self._param_def_block.append(frame.symbols.dump_param_targets())
 742
 743    def pop_parameter_definitions(self) -> None:
 744        """Pops the current parameter definitions set."""
 745        self._param_def_block.pop()
 746
 747    def mark_parameter_stored(self, target: str) -> None:
 748        """Marks a parameter in the current parameter definitions as stored.
 749        This will skip the enforced undefined checks.
 750        """
 751        if self._param_def_block:
 752            self._param_def_block[-1].discard(target)
 753
 754    def push_context_reference(self, target: str) -> None:
 755        self._context_reference_stack.append(target)
 756
 757    def pop_context_reference(self) -> None:
 758        self._context_reference_stack.pop()
 759
 760    def get_context_ref(self) -> str:
 761        return self._context_reference_stack[-1]
 762
 763    def get_resolve_func(self) -> str:
 764        target = self._context_reference_stack[-1]
 765        if target == "context":
 766            return "resolve"
 767        return f"{target}.resolve"
 768
 769    def derive_context(self, frame: Frame) -> str:
 770        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
 771
 772    def parameter_is_undeclared(self, target: str) -> bool:
 773        """Checks if a given target is an undeclared parameter."""
 774        if not self._param_def_block:
 775            return False
 776        return target in self._param_def_block[-1]
 777
 778    def push_assign_tracking(self) -> None:
 779        """Pushes a new layer for assignment tracking."""
 780        self._assign_stack.append(set())
 781
 782    def pop_assign_tracking(self, frame: Frame) -> None:
 783        """Pops the topmost level for assignment tracking and updates the
 784        context variables if necessary.
 785        """
 786        vars = self._assign_stack.pop()
 787        if (
 788            not frame.block_frame
 789            and not frame.loop_frame
 790            and not frame.toplevel
 791            or not vars
 792        ):
 793            return
 794        public_names = [x for x in vars if x[:1] != "_"]
 795        if len(vars) == 1:
 796            name = next(iter(vars))
 797            ref = frame.symbols.ref(name)
 798            if frame.loop_frame:
 799                self.writeline(f"_loop_vars[{name!r}] = {ref}")
 800                return
 801            if frame.block_frame:
 802                self.writeline(f"_block_vars[{name!r}] = {ref}")
 803                return
 804            self.writeline(f"context.vars[{name!r}] = {ref}")
 805        else:
 806            if frame.loop_frame:
 807                self.writeline("_loop_vars.update({")
 808            elif frame.block_frame:
 809                self.writeline("_block_vars.update({")
 810            else:
 811                self.writeline("context.vars.update({")
 812            for idx, name in enumerate(vars):
 813                if idx:
 814                    self.write(", ")
 815                ref = frame.symbols.ref(name)
 816                self.write(f"{name!r}: {ref}")
 817            self.write("})")
 818        if not frame.block_frame and not frame.loop_frame and public_names:
 819            if len(public_names) == 1:
 820                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
 821            else:
 822                names_str = ", ".join(map(repr, public_names))
 823                self.writeline(f"context.exported_vars.update(({names_str}))")
 824
 825    # -- Statement Visitors
 826
 827    def visit_Template(
 828        self, node: nodes.Template, frame: t.Optional[Frame] = None
 829    ) -> None:
 830        assert frame is None, "no root frame allowed"
 831        eval_ctx = EvalContext(self.environment, self.name)
 832
 833        from .runtime import exported, async_exported
 834
 835        if self.environment.is_async:
 836            exported_names = sorted(exported + async_exported)
 837        else:
 838            exported_names = sorted(exported)
 839
 840        self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
 841
 842        # if we want a deferred initialization we cannot move the
 843        # environment into a local name
 844        envenv = "" if self.defer_init else ", environment=environment"
 845
 846        # do we have an extends tag at all?  If not, we can save some
 847        # overhead by just not processing any inheritance code.
 848        have_extends = node.find(nodes.Extends) is not None
 849
 850        # find all blocks
 851        for block in node.find_all(nodes.Block):
 852            if block.name in self.blocks:
 853                self.fail(f"block {block.name!r} defined twice", block.lineno)
 854            self.blocks[block.name] = block
 855
 856        # find all imports and import them
 857        for import_ in node.find_all(nodes.ImportedName):
 858            if import_.importname not in self.import_aliases:
 859                imp = import_.importname
 860                self.import_aliases[imp] = alias = self.temporary_identifier()
 861                if "." in imp:
 862                    module, obj = imp.rsplit(".", 1)
 863                    self.writeline(f"from {module} import {obj} as {alias}")
 864                else:
 865                    self.writeline(f"import {imp} as {alias}")
 866
 867        # add the load name
 868        self.writeline(f"name = {self.name!r}")
 869
 870        # generate the root render function.
 871        self.writeline(
 872            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
 873        )
 874        self.indent()
 875        self.write_commons()
 876
 877        # process the root
 878        frame = Frame(eval_ctx)
 879        if "self" in find_undeclared(node.body, ("self",)):
 880            ref = frame.symbols.declare_parameter("self")
 881            self.writeline(f"{ref} = TemplateReference(context)")
 882        frame.symbols.analyze_node(node)
 883        frame.toplevel = frame.rootlevel = True
 884        frame.require_output_check = have_extends and not self.has_known_extends
 885        if have_extends:
 886            self.writeline("parent_template = None")
 887        self.enter_frame(frame)
 888        self.pull_dependencies(node.body)
 889        self.blockvisit(node.body, frame)
 890        self.leave_frame(frame, with_python_scope=True)
 891        self.outdent()
 892
 893        # make sure that the parent root is called.
 894        if have_extends:
 895            if not self.has_known_extends:
 896                self.indent()
 897                self.writeline("if parent_template is not None:")
 898            self.indent()
 899            if not self.environment.is_async:
 900                self.writeline("yield from parent_template.root_render_func(context)")
 901            else:
 902                self.writeline(
 903                    "async for event in parent_template.root_render_func(context):"
 904                )
 905                self.indent()
 906                self.writeline("yield event")
 907                self.outdent()
 908            self.outdent(1 + (not self.has_known_extends))
 909
 910        # at this point we now have the blocks collected and can visit them too.
 911        for name, block in self.blocks.items():
 912            self.writeline(
 913                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
 914                block,
 915                1,
 916            )
 917            self.indent()
 918            self.write_commons()
 919            # It's important that we do not make this frame a child of the
 920            # toplevel template.  This would cause a variety of
 921            # interesting issues with identifier tracking.
 922            block_frame = Frame(eval_ctx)
 923            block_frame.block_frame = True
 924            undeclared = find_undeclared(block.body, ("self", "super"))
 925            if "self" in undeclared:
 926                ref = block_frame.symbols.declare_parameter("self")
 927                self.writeline(f"{ref} = TemplateReference(context)")
 928            if "super" in undeclared:
 929                ref = block_frame.symbols.declare_parameter("super")
 930                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
 931            block_frame.symbols.analyze_node(block)
 932            block_frame.block = name
 933            self.writeline("_block_vars = {}")
 934            self.enter_frame(block_frame)
 935            self.pull_dependencies(block.body)
 936            self.blockvisit(block.body, block_frame)
 937            self.leave_frame(block_frame, with_python_scope=True)
 938            self.outdent()
 939
 940        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
 941        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
 942        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
 943        self.writeline(f"debug_info = {debug_kv_str!r}")
 944
 945    def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
 946        """Call a block and register it for the template."""
 947        level = 0
 948        if frame.toplevel:
 949            # if we know that we are a child template, there is no need to
 950            # check if we are one
 951            if self.has_known_extends:
 952                return
 953            if self.extends_so_far > 0:
 954                self.writeline("if parent_template is None:")
 955                self.indent()
 956                level += 1
 957
 958        if node.scoped:
 959            context = self.derive_context(frame)
 960        else:
 961            context = self.get_context_ref()
 962
 963        if node.required:
 964            self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
 965            self.indent()
 966            self.writeline(
 967                f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
 968                node,
 969            )
 970            self.outdent()
 971
 972        if not self.environment.is_async and frame.buffer is None:
 973            self.writeline(
 974                f"yield from context.blocks[{node.name!r}][0]({context})", node
 975            )
 976        else:
 977            self.writeline(
 978                f"{self.choose_async()}for event in"
 979                f" context.blocks[{node.name!r}][0]({context}):",
 980                node,
 981            )
 982            self.indent()
 983            self.simple_write("event", frame)
 984            self.outdent()
 985
 986        self.outdent(level)
 987
 988    def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
 989        """Calls the extender."""
 990        if not frame.toplevel:
 991            self.fail("cannot use extend from a non top-level scope", node.lineno)
 992
 993        # if the number of extends statements in general is zero so
 994        # far, we don't have to add a check if something extended
 995        # the template before this one.
 996        if self.extends_so_far > 0:
 997            # if we have a known extends we just add a template runtime
 998            # error into the generated code.  We could catch that at compile
 999            # time too, but i welcome it not to confuse users by throwing the
1000            # same error at different times just "because we can".
1001            if not self.has_known_extends:
1002                self.writeline("if parent_template is not None:")
1003                self.indent()
1004            self.writeline('raise TemplateRuntimeError("extended multiple times")')
1005
1006            # if we have a known extends already we don't need that code here
1007            # as we know that the template execution will end here.
1008            if self.has_known_extends:
1009                raise CompilerExit()
1010            else:
1011                self.outdent()
1012
1013        self.writeline("parent_template = environment.get_template(", node)
1014        self.visit(node.template, frame)
1015        self.write(f", {self.name!r})")
1016        self.writeline("for name, parent_block in parent_template.blocks.items():")
1017        self.indent()
1018        self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
1019        self.outdent()
1020
1021        # if this extends statement was in the root level we can take
1022        # advantage of that information and simplify the generated code
1023        # in the top level from this point onwards
1024        if frame.rootlevel:
1025            self.has_known_extends = True
1026
1027        # and now we have one more
1028        self.extends_so_far += 1
1029
1030    def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1031        """Handles includes."""
1032        if node.ignore_missing:
1033            self.writeline("try:")
1034            self.indent()
1035
1036        func_name = "get_or_select_template"
1037        if isinstance(node.template, nodes.Const):
1038            if isinstance(node.template.value, str):
1039                func_name = "get_template"
1040            elif isinstance(node.template.value, (tuple, list)):
1041                func_name = "select_template"
1042        elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1043            func_name = "select_template"
1044
1045        self.writeline(f"template = environment.{func_name}(", node)
1046        self.visit(node.template, frame)
1047        self.write(f", {self.name!r})")
1048        if node.ignore_missing:
1049            self.outdent()
1050            self.writeline("except TemplateNotFound:")
1051            self.indent()
1052            self.writeline("pass")
1053            self.outdent()
1054            self.writeline("else:")
1055            self.indent()
1056
1057        skip_event_yield = False
1058        if node.with_context:
1059            self.writeline(
1060                f"{self.choose_async()}for event in template.root_render_func("
1061                "template.new_context(context.get_all(), True,"
1062                f" {self.dump_local_context(frame)})):"
1063            )
1064        elif self.environment.is_async:
1065            self.writeline(
1066                "for event in (await template._get_default_module_async())"
1067                "._body_stream:"
1068            )
1069        else:
1070            self.writeline("yield from template._get_default_module()._body_stream")
1071            skip_event_yield = True
1072
1073        if not skip_event_yield:
1074            self.indent()
1075            self.simple_write("event", frame)
1076            self.outdent()
1077
1078        if node.ignore_missing:
1079            self.outdent()
1080
1081    def _import_common(
1082        self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
1083    ) -> None:
1084        self.write(f"{self.choose_async('await ')}environment.get_template(")
1085        self.visit(node.template, frame)
1086        self.write(f", {self.name!r}).")
1087
1088        if node.with_context:
1089            f_name = f"make_module{self.choose_async('_async')}"
1090            self.write(
1091                f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
1092            )
1093        else:
1094            self.write(f"_get_default_module{self.choose_async('_async')}(context)")
1095
1096    def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1097        """Visit regular imports."""
1098        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1099        if frame.toplevel:
1100            self.write(f"context.vars[{node.target!r}] = ")
1101
1102        self._import_common(node, frame)
1103
1104        if frame.toplevel and not node.target.startswith("_"):
1105            self.writeline(f"context.exported_vars.discard({node.target!r})")
1106
1107    def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1108        """Visit named imports."""
1109        self.newline(node)
1110        self.write("included_template = ")
1111        self._import_common(node, frame)
1112        var_names = []
1113        discarded_names = []
1114        for name in node.names:
1115            if isinstance(name, tuple):
1116                name, alias = name
1117            else:
1118                alias = name
1119            self.writeline(
1120                f"{frame.symbols.ref(alias)} ="
1121                f" getattr(included_template, {name!r}, missing)"
1122            )
1123            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1124            self.indent()
1125            message = (
1126                "the template {included_template.__name__!r}"
1127                f" (imported on {self.position(node)})"
1128                f" does not export the requested name {name!r}"
1129            )
1130            self.writeline(
1131                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1132            )
1133            self.outdent()
1134            if frame.toplevel:
1135                var_names.append(alias)
1136                if not alias.startswith("_"):
1137                    discarded_names.append(alias)
1138
1139        if var_names:
1140            if len(var_names) == 1:
1141                name = var_names[0]
1142                self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1143            else:
1144                names_kv = ", ".join(
1145                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1146                )
1147                self.writeline(f"context.vars.update({{{names_kv}}})")
1148        if discarded_names:
1149            if len(discarded_names) == 1:
1150                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1151            else:
1152                names_str = ", ".join(map(repr, discarded_names))
1153                self.writeline(
1154                    f"context.exported_vars.difference_update(({names_str}))"
1155                )
1156
1157    def visit_For(self, node: nodes.For, frame: Frame) -> None:
1158        loop_frame = frame.inner()
1159        loop_frame.loop_frame = True
1160        test_frame = frame.inner()
1161        else_frame = frame.inner()
1162
1163        # try to figure out if we have an extended loop.  An extended loop
1164        # is necessary if the loop is in recursive mode if the special loop
1165        # variable is accessed in the body if the body is a scoped block.
1166        extended_loop = (
1167            node.recursive
1168            or "loop"
1169            in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1170            or any(block.scoped for block in node.find_all(nodes.Block))
1171        )
1172
1173        loop_ref = None
1174        if extended_loop:
1175            loop_ref = loop_frame.symbols.declare_parameter("loop")
1176
1177        loop_frame.symbols.analyze_node(node, for_branch="body")
1178        if node.else_:
1179            else_frame.symbols.analyze_node(node, for_branch="else")
1180
1181        if node.test:
1182            loop_filter_func = self.temporary_identifier()
1183            test_frame.symbols.analyze_node(node, for_branch="test")
1184            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1185            self.indent()
1186            self.enter_frame(test_frame)
1187            self.writeline(self.choose_async("async for ", "for "))
1188            self.visit(node.target, loop_frame)
1189            self.write(" in ")
1190            self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1191            self.write(":")
1192            self.indent()
1193            self.writeline("if ", node.test)
1194            self.visit(node.test, test_frame)
1195            self.write(":")
1196            self.indent()
1197            self.writeline("yield ")
1198            self.visit(node.target, loop_frame)
1199            self.outdent(3)
1200            self.leave_frame(test_frame, with_python_scope=True)
1201
1202        # if we don't have an recursive loop we have to find the shadowed
1203        # variables at that point.  Because loops can be nested but the loop
1204        # variable is a special one we have to enforce aliasing for it.
1205        if node.recursive:
1206            self.writeline(
1207                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1208            )
1209            self.indent()
1210            self.buffer(loop_frame)
1211
1212            # Use the same buffer for the else frame
1213            else_frame.buffer = loop_frame.buffer
1214
1215        # make sure the loop variable is a special one and raise a template
1216        # assertion error if a loop tries to write to loop
1217        if extended_loop:
1218            self.writeline(f"{loop_ref} = missing")
1219
1220        for name in node.find_all(nodes.Name):
1221            if name.ctx == "store" and name.name == "loop":
1222                self.fail(
1223                    "Can't assign to special loop variable in for-loop target",
1224                    name.lineno,
1225                )
1226
1227        if node.else_:
1228            iteration_indicator = self.temporary_identifier()
1229            self.writeline(f"{iteration_indicator} = 1")
1230
1231        self.writeline(self.choose_async("async for ", "for "), node)
1232        self.visit(node.target, loop_frame)
1233        if extended_loop:
1234            self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1235        else:
1236            self.write(" in ")
1237
1238        if node.test:
1239            self.write(f"{loop_filter_func}(")
1240        if node.recursive:
1241            self.write("reciter")
1242        else:
1243            if self.environment.is_async and not extended_loop:
1244                self.write("auto_aiter(")
1245            self.visit(node.iter, frame)
1246            if self.environment.is_async and not extended_loop:
1247                self.write(")")
1248        if node.test:
1249            self.write(")")
1250
1251        if node.recursive:
1252            self.write(", undefined, loop_render_func, depth):")
1253        else:
1254            self.write(", undefined):" if extended_loop else ":")
1255
1256        self.indent()
1257        self.enter_frame(loop_frame)
1258
1259        self.writeline("_loop_vars = {}")
1260        self.blockvisit(node.body, loop_frame)
1261        if node.else_:
1262            self.writeline(f"{iteration_indicator} = 0")
1263        self.outdent()
1264        self.leave_frame(
1265            loop_frame, with_python_scope=node.recursive and not node.else_
1266        )
1267
1268        if node.else_:
1269            self.writeline(f"if {iteration_indicator}:")
1270            self.indent()
1271            self.enter_frame(else_frame)
1272            self.blockvisit(node.else_, else_frame)
1273            self.leave_frame(else_frame)
1274            self.outdent()
1275
1276        # if the node was recursive we have to return the buffer contents
1277        # and start the iteration code
1278        if node.recursive:
1279            self.return_buffer_contents(loop_frame)
1280            self.outdent()
1281            self.start_write(frame, node)
1282            self.write(f"{self.choose_async('await ')}loop(")
1283            if self.environment.is_async:
1284                self.write("auto_aiter(")
1285            self.visit(node.iter, frame)
1286            if self.environment.is_async:
1287                self.write(")")
1288            self.write(", loop)")
1289            self.end_write(frame)
1290
1291        # at the end of the iteration, clear any assignments made in the
1292        # loop from the top level
1293        if self._assign_stack:
1294            self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1295
1296    def visit_If(self, node: nodes.If, frame: Frame) -> None:
1297        if_frame = frame.soft()
1298        self.writeline("if ", node)
1299        self.visit(node.test, if_frame)
1300        self.write(":")
1301        self.indent()
1302        self.blockvisit(node.body, if_frame)
1303        self.outdent()
1304        for elif_ in node.elif_:
1305            self.writeline("elif ", elif_)
1306            self.visit(elif_.test, if_frame)
1307            self.write(":")
1308            self.indent()
1309            self.blockvisit(elif_.body, if_frame)
1310            self.outdent()
1311        if node.else_:
1312            self.writeline("else:")
1313            self.indent()
1314            self.blockvisit(node.else_, if_frame)
1315            self.outdent()
1316
1317    def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1318        macro_frame, macro_ref = self.macro_body(node, frame)
1319        self.newline()
1320        if frame.toplevel:
1321            if not node.name.startswith("_"):
1322                self.write(f"context.exported_vars.add({node.name!r})")
1323            self.writeline(f"context.vars[{node.name!r}] = ")
1324        self.write(f"{frame.symbols.ref(node.name)} = ")
1325        self.macro_def(macro_ref, macro_frame)
1326
1327    def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1328        call_frame, macro_ref = self.macro_body(node, frame)
1329        self.writeline("caller = ")
1330        self.macro_def(macro_ref, call_frame)
1331        self.start_write(frame, node)
1332        self.visit_Call(node.call, frame, forward_caller=True)
1333        self.end_write(frame)
1334
1335    def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1336        filter_frame = frame.inner()
1337        filter_frame.symbols.analyze_node(node)
1338        self.enter_frame(filter_frame)
1339        self.buffer(filter_frame)
1340        self.blockvisit(node.body, filter_frame)
1341        self.start_write(frame, node)
1342        self.visit_Filter(node.filter, filter_frame)
1343        self.end_write(frame)
1344        self.leave_frame(filter_frame)
1345
1346    def visit_With(self, node: nodes.With, frame: Frame) -> None:
1347        with_frame = frame.inner()
1348        with_frame.symbols.analyze_node(node)
1349        self.enter_frame(with_frame)
1350        for target, expr in zip(node.targets, node.values):
1351            self.newline()
1352            self.visit(target, with_frame)
1353            self.write(" = ")
1354            self.visit(expr, frame)
1355        self.blockvisit(node.body, with_frame)
1356        self.leave_frame(with_frame)
1357
1358    def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1359        self.newline(node)
1360        self.visit(node.node, frame)
1361
1362    class _FinalizeInfo(t.NamedTuple):
1363        const: t.Optional[t.Callable[..., str]]
1364        src: t.Optional[str]
1365
1366    @staticmethod
1367    def _default_finalize(value: t.Any) -> t.Any:
1368        """The default finalize function if the environment isn't
1369        configured with one. Or, if the environment has one, this is
1370        called on that function's output for constants.
1371        """
1372        return str(value)
1373
1374    _finalize: t.Optional[_FinalizeInfo] = None
1375
1376    def _make_finalize(self) -> _FinalizeInfo:
1377        """Build the finalize function to be used on constants and at
1378        runtime. Cached so it's only created once for all output nodes.
1379
1380        Returns a ``namedtuple`` with the following attributes:
1381
1382        ``const``
1383            A function to finalize constant data at compile time.
1384
1385        ``src``
1386            Source code to output around nodes to be evaluated at
1387            runtime.
1388        """
1389        if self._finalize is not None:
1390            return self._finalize
1391
1392        finalize: t.Optional[t.Callable[..., t.Any]]
1393        finalize = default = self._default_finalize
1394        src = None
1395
1396        if self.environment.finalize:
1397            src = "environment.finalize("
1398            env_finalize = self.environment.finalize
1399            pass_arg = {
1400                _PassArg.context: "context",
1401                _PassArg.eval_context: "context.eval_ctx",
1402                _PassArg.environment: "environment",
1403            }.get(
1404                _PassArg.from_obj(env_finalize)  # type: ignore
1405            )
1406            finalize = None
1407
1408            if pass_arg is None:
1409
1410                def finalize(value: t.Any) -> t.Any:  # noqa: F811
1411                    return default(env_finalize(value))
1412
1413            else:
1414                src = f"{src}{pass_arg}, "
1415
1416                if pass_arg == "environment":
1417
1418                    def finalize(value: t.Any) -> t.Any:  # noqa: F811
1419                        return default(env_finalize(self.environment, value))
1420
1421        self._finalize = self._FinalizeInfo(finalize, src)
1422        return self._finalize
1423
1424    def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
1425        """Given a group of constant values converted from ``Output``
1426        child nodes, produce a string to write to the template module
1427        source.
1428        """
1429        return repr(concat(group))
1430
1431    def _output_child_to_const(
1432        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1433    ) -> str:
1434        """Try to optimize a child of an ``Output`` node by trying to
1435        convert it to constant, finalized data at compile time.
1436
1437        If :exc:`Impossible` is raised, the node is not constant and
1438        will be evaluated at runtime. Any other exception will also be
1439        evaluated at runtime for easier debugging.
1440        """
1441        const = node.as_const(frame.eval_ctx)
1442
1443        if frame.eval_ctx.autoescape:
1444            const = escape(const)
1445
1446        # Template data doesn't go through finalize.
1447        if isinstance(node, nodes.TemplateData):
1448            return str(const)
1449
1450        return finalize.const(const)  # type: ignore
1451
1452    def _output_child_pre(
1453        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1454    ) -> None:
1455        """Output extra source code before visiting a child of an
1456        ``Output`` node.
1457        """
1458        if frame.eval_ctx.volatile:
1459            self.write("(escape if context.eval_ctx.autoescape else str)(")
1460        elif frame.eval_ctx.autoescape:
1461            self.write("escape(")
1462        else:
1463            self.write("str(")
1464
1465        if finalize.src is not None:
1466            self.write(finalize.src)
1467
1468    def _output_child_post(
1469        self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1470    ) -> None:
1471        """Output extra source code after visiting a child of an
1472        ``Output`` node.
1473        """
1474        self.write(")")
1475
1476        if finalize.src is not None:
1477            self.write(")")
1478
1479    def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1480        # If an extends is active, don't render outside a block.
1481        if frame.require_output_check:
1482            # A top-level extends is known to exist at compile time.
1483            if self.has_known_extends:
1484                return
1485
1486            self.writeline("if parent_template is None:")
1487            self.indent()
1488
1489        finalize = self._make_finalize()
1490        body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1491
1492        # Evaluate constants at compile time if possible. Each item in
1493        # body will be either a list of static data or a node to be
1494        # evaluated at runtime.
1495        for child in node.nodes:
1496            try:
1497                if not (
1498                    # If the finalize function requires runtime context,
1499                    # constants can't be evaluated at compile time.
1500                    finalize.const
1501                    # Unless it's basic template data that won't be
1502                    # finalized anyway.
1503                    or isinstance(child, nodes.TemplateData)
1504                ):
1505                    raise nodes.Impossible()
1506
1507                const = self._output_child_to_const(child, frame, finalize)
1508            except (nodes.Impossible, Exception):
1509                # The node was not constant and needs to be evaluated at
1510                # runtime. Or another error was raised, which is easier
1511                # to debug at runtime.
1512                body.append(child)
1513                continue
1514
1515            if body and isinstance(body[-1], list):
1516                body[-1].append(const)
1517            else:
1518                body.append([const])
1519
1520        if frame.buffer is not None:
1521            if len(body) == 1:
1522                self.writeline(f"{frame.buffer}.append(")
1523            else:
1524                self.writeline(f"{frame.buffer}.extend((")
1525
1526            self.indent()
1527
1528        for item in body:
1529            if isinstance(item, list):
1530                # A group of constant data to join and output.
1531                val = self._output_const_repr(item)
1532
1533                if frame.buffer is None:
1534                    self.writeline("yield " + val)
1535                else:
1536                    self.writeline(val + ",")
1537            else:
1538                if frame.buffer is None:
1539                    self.writeline("yield ", item)
1540                else:
1541                    self.newline(item)
1542
1543                # A node to be evaluated at runtime.
1544                self._output_child_pre(item, frame, finalize)
1545                self.visit(item, frame)
1546                self._output_child_post(item, frame, finalize)
1547
1548                if frame.buffer is not None:
1549                    self.write(",")
1550
1551        if frame.buffer is not None:
1552            self.outdent()
1553            self.writeline(")" if len(body) == 1 else "))")
1554
1555        if frame.require_output_check:
1556            self.outdent()
1557
1558    def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1559        self.push_assign_tracking()
1560        self.newline(node)
1561        self.visit(node.target, frame)
1562        self.write(" = ")
1563        self.visit(node.node, frame)
1564        self.pop_assign_tracking(frame)
1565
1566    def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1567        self.push_assign_tracking()
1568        block_frame = frame.inner()
1569        # This is a special case.  Since a set block always captures we
1570        # will disable output checks.  This way one can use set blocks
1571        # toplevel even in extended templates.
1572        block_frame.require_output_check = False
1573        block_frame.symbols.analyze_node(node)
1574        self.enter_frame(block_frame)
1575        self.buffer(block_frame)
1576        self.blockvisit(node.body, block_frame)
1577        self.newline(node)
1578        self.visit(node.target, frame)
1579        self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1580        if node.filter is not None:
1581            self.visit_Filter(node.filter, block_frame)
1582        else:
1583            self.write(f"concat({block_frame.buffer})")
1584        self.write(")")
1585        self.pop_assign_tracking(frame)
1586        self.leave_frame(block_frame)
1587
1588    # -- Expression Visitors
1589
1590    def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1591        if node.ctx == "store" and (
1592            frame.toplevel or frame.loop_frame or frame.block_frame
1593        ):
1594            if self._assign_stack:
1595                self._assign_stack[-1].add(node.name)
1596        ref = frame.symbols.ref(node.name)
1597
1598        # If we are looking up a variable we might have to deal with the
1599        # case where it's undefined.  We can skip that case if the load
1600        # instruction indicates a parameter which are always defined.
1601        if node.ctx == "load":
1602            load = frame.symbols.find_load(ref)
1603            if not (
1604                load is not None
1605                and load[0] == VAR_LOAD_PARAMETER
1606                and not self.parameter_is_undeclared(ref)
1607            ):
1608                self.write(
1609                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1610                )
1611                return
1612
1613        self.write(ref)
1614
1615    def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1616        # NSRefs can only be used to store values; since they use the normal
1617        # `foo.bar` notation they will be parsed as a normal attribute access
1618        # when used anywhere but in a `set` context
1619        ref = frame.symbols.ref(node.name)
1620        self.writeline(f"if not isinstance({ref}, Namespace):")
1621        self.indent()
1622        self.writeline(
1623            "raise TemplateRuntimeError"
1624            '("cannot assign attribute on non-namespace object")'
1625        )
1626        self.outdent()
1627        self.writeline(f"{ref}[{node.attr!r}]")
1628
1629    def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1630        val = node.as_const(frame.eval_ctx)
1631        if isinstance(val, float):
1632            self.write(str(val))
1633        else:
1634            self.write(repr(val))
1635
1636    def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1637        try:
1638            self.write(repr(node.as_const(frame.eval_ctx)))
1639        except nodes.Impossible:
1640            self.write(
1641                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1642            )
1643
1644    def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1645        self.write("(")
1646        idx = -1
1647        for idx, item in enumerate(node.items):
1648            if idx:
1649                self.write(", ")
1650            self.visit(item, frame)
1651        self.write(",)" if idx == 0 else ")")
1652
1653    def visit_List(self, node: nodes.List, frame: Frame) -> None:
1654        self.write("[")
1655        for idx, item in enumerate(node.items):
1656            if idx:
1657                self.write(", ")
1658            self.visit(item, frame)
1659        self.write("]")
1660
1661    def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1662        self.write("{")
1663        for idx, item in enumerate(node.items):
1664            if idx:
1665                self.write(", ")
1666            self.visit(item.key, frame)
1667            self.write(": ")
1668            self.visit(item.value, frame)
1669        self.write("}")
1670
1671    visit_Add = _make_binop("+")
1672    visit_Sub = _make_binop("-")
1673    visit_Mul = _make_binop("*")
1674    visit_Div = _make_binop("/")
1675    visit_FloorDiv = _make_binop("//")
1676    visit_Pow = _make_binop("**")
1677    visit_Mod = _make_binop("%")
1678    visit_And = _make_binop("and")
1679    visit_Or = _make_binop("or")
1680    visit_Pos = _make_unop("+")
1681    visit_Neg = _make_unop("-")
1682    visit_Not = _make_unop("not ")
1683
1684    @optimizeconst
1685    def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1686        if frame.eval_ctx.volatile:
1687            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1688        elif frame.eval_ctx.autoescape:
1689            func_name = "markup_join"
1690        else:
1691            func_name = "str_join"
1692        self.write(f"{func_name}((")
1693        for arg in node.nodes:
1694            self.visit(arg, frame)
1695            self.write(", ")
1696        self.write("))")
1697
1698    @optimizeconst
1699    def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1700        self.write("(")
1701        self.visit(node.expr, frame)
1702        for op in node.ops:
1703            self.visit(op, frame)
1704        self.write(")")
1705
1706    def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1707        self.write(f" {operators[node.op]} ")
1708        self.visit(node.expr, frame)
1709
1710    @optimizeconst
1711    def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1712        if self.environment.is_async:
1713            self.write("(await auto_await(")
1714
1715        self.write("environment.getattr(")
1716        self.visit(node.node, frame)
1717        self.write(f", {node.attr!r})")
1718
1719        if self.environment.is_async:
1720            self.write("))")
1721
1722    @optimizeconst
1723    def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1724        # slices bypass the environment getitem method.
1725        if isinstance(node.arg, nodes.Slice):
1726            self.visit(node.node, frame)
1727            self.write("[")
1728            self.visit(node.arg, frame)
1729            self.write("]")
1730        else:
1731            if self.environment.is_async:
1732                self.write("(await auto_await(")
1733
1734            self.write("environment.getitem(")
1735            self.visit(node.node, frame)
1736            self.write(", ")
1737            self.visit(node.arg, frame)
1738            self.write(")")
1739
1740            if self.environment.is_async:
1741                self.write("))")
1742
1743    def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1744        if node.start is not None:
1745            self.visit(node.start, frame)
1746        self.write(":")
1747        if node.stop is not None:
1748            self.visit(node.stop, frame)
1749        if node.step is not None:
1750            self.write(":")
1751            self.visit(node.step, frame)
1752
1753    @contextmanager
1754    def _filter_test_common(
1755        self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
1756    ) -> t.Iterator[None]:
1757        if self.environment.is_async:
1758            self.write("(await auto_await(")
1759
1760        if is_filter:
1761            self.write(f"{self.filters[node.name]}(")
1762            func = self.environment.filters.get(node.name)
1763        else:
1764            self.write(f"{self.tests[node.name]}(")
1765            func = self.environment.tests.get(node.name)
1766
1767        # When inside an If or CondExpr frame, allow the filter to be
1768        # undefined at compile time and only raise an error if it's
1769        # actually called at runtime. See pull_dependencies.
1770        if func is None and not frame.soft_frame:
1771            type_name = "filter" if is_filter else "test"
1772            self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
1773
1774        pass_arg = {
1775            _PassArg.context: "context",
1776            _PassArg.eval_context: "context.eval_ctx",
1777            _PassArg.environment: "environment",
1778        }.get(
1779            _PassArg.from_obj(func)  # type: ignore
1780        )
1781
1782        if pass_arg is not None:
1783            self.write(f"{pass_arg}, ")
1784
1785        # Back to the visitor function to handle visiting the target of
1786        # the filter or test.
1787        yield
1788
1789        self.signature(node, frame)
1790        self.write(")")
1791
1792        if self.environment.is_async:
1793            self.write("))")
1794
1795    @optimizeconst
1796    def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1797        with self._filter_test_common(node, frame, True):
1798            # if the filter node is None we are inside a filter block
1799            # and want to write to the current buffer
1800            if node.node is not None:
1801                self.visit(node.node, frame)
1802            elif frame.eval_ctx.volatile:
1803                self.write(
1804                    f"(Markup(concat({frame.buffer}))"
1805                    f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1806                )
1807            elif frame.eval_ctx.autoescape:
1808                self.write(f"Markup(concat({frame.buffer}))")
1809            else:
1810                self.write(f"concat({frame.buffer})")
1811
1812    @optimizeconst
1813    def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1814        with self._filter_test_common(node, frame, False):
1815            self.visit(node.node, frame)
1816
1817    @optimizeconst
1818    def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1819        frame = frame.soft()
1820
1821        def write_expr2() -> None:
1822            if node.expr2 is not None:
1823                self.visit(node.expr2, frame)
1824                return
1825
1826            self.write(
1827                f'cond_expr_undefined("the inline if-expression on'
1828                f" {self.position(node)} evaluated to false and no else"
1829                f' section was defined.")'
1830            )
1831
1832        self.write("(")
1833        self.visit(node.expr1, frame)
1834        self.write(" if ")
1835        self.visit(node.test, frame)
1836        self.write(" else ")
1837        write_expr2()
1838        self.write(")")
1839
1840    @optimizeconst
1841    def visit_Call(
1842        self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1843    ) -> None:
1844        if self.environment.is_async:
1845            self.write("(await auto_await(")
1846        if self.environment.sandboxed:
1847            self.write("environment.call(context, ")
1848        else:
1849            self.write("context.call(")
1850        self.visit(node.node, frame)
1851        extra_kwargs = {"caller": "caller"} if forward_caller else None
1852        loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1853        block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1854        if extra_kwargs:
1855            extra_kwargs.update(loop_kwargs, **block_kwargs)
1856        elif loop_kwargs or block_kwargs:
1857            extra_kwargs = dict(loop_kwargs, **block_kwargs)
1858        self.signature(node, frame, extra_kwargs)
1859        self.write(")")
1860        if self.environment.is_async:
1861            self.write("))")
1862
1863    def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1864        self.write(node.key + "=")
1865        self.visit(node.value, frame)
1866
1867    # -- Unused nodes for extensions
1868
1869    def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1870        self.write("Markup(")
1871        self.visit(node.expr, frame)
1872        self.write(")")
1873
1874    def visit_MarkSafeIfAutoescape(
1875        self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1876    ) -> None:
1877        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1878        self.visit(node.expr, frame)
1879        self.write(")")
1880
1881    def visit_EnvironmentAttribute(
1882        self, node: nodes.EnvironmentAttribute, frame: Frame
1883    ) -> None:
1884        self.write("environment." + node.name)
1885
1886    def visit_ExtensionAttribute(
1887        self, node: nodes.ExtensionAttribute, frame: Frame
1888    ) -> None:
1889        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
1890
1891    def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1892        self.write(self.import_aliases[node.importname])
1893
1894    def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1895        self.write(node.name)
1896
1897    def visit_ContextReference(
1898        self, node: nodes.ContextReference, frame: Frame
1899    ) -> None:
1900        self.write("context")
1901
1902    def visit_DerivedContextReference(
1903        self, node: nodes.DerivedContextReference, frame: Frame
1904    ) -> None:
1905        self.write(self.derive_context(frame))
1906
1907    def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1908        self.writeline("continue", node)
1909
1910    def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1911        self.writeline("break", node)
1912
1913    def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1914        scope_frame = frame.inner()
1915        scope_frame.symbols.analyze_node(node)
1916        self.enter_frame(scope_frame)
1917        self.blockvisit(node.body, scope_frame)
1918        self.leave_frame(scope_frame)
1919
1920    def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1921        ctx = self.temporary_identifier()
1922        self.writeline(f"{ctx} = {self.derive_context(frame)}")
1923        self.writeline(f"{ctx}.vars = ")
1924        self.visit(node.context, frame)
1925        self.push_context_reference(ctx)
1926
1927        scope_frame = frame.inner(isolated=True)
1928        scope_frame.symbols.analyze_node(node)
1929        self.enter_frame(scope_frame)
1930        self.blockvisit(node.body, scope_frame)
1931        self.leave_frame(scope_frame)
1932        self.pop_context_reference()
1933
1934    def visit_EvalContextModifier(
1935        self, node: nodes.EvalContextModifier, frame: Frame
1936    ) -> None:
1937        for keyword in node.options:
1938            self.writeline(f"context.eval_ctx.{keyword.key} = ")
1939            self.visit(keyword.value, frame)
1940            try:
1941                val = keyword.value.as_const(frame.eval_ctx)
1942            except nodes.Impossible:
1943                frame.eval_ctx.volatile = True
1944            else:
1945                setattr(frame.eval_ctx, keyword.key, val)
1946
1947    def visit_ScopedEvalContextModifier(
1948        self, node: nodes.ScopedEvalContextModifier, frame: Frame
1949    ) -> None:
1950        old_ctx_name = self.temporary_identifier()
1951        saved_ctx = frame.eval_ctx.save()
1952        self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1953        self.visit_EvalContextModifier(node, frame)
1954        for child in node.body:
1955            self.visit(child, frame)
1956        frame.eval_ctx.revert(saved_ctx)
1957        self.writeline(f"context.eval_ctx.revert({old_ctx_name})")

Walks the abstract syntax tree and call visitor functions for every node found. The visitor functions may return values which will be forwarded by the visit method.

Per default the visitor functions for the nodes are 'visit_' + class name of the node. So a TryFinally node visit function would be visit_TryFinally. This behavior can be changed by overriding the get_visitor function. If no visitor function exists for a node (return value None) the generic_visit visitor is used instead.

CodeGenerator( environment: jinja2.environment.Environment, name: Optional[str], filename: Optional[str], stream: Optional[TextIO] = None, defer_init: bool = False, optimized: bool = True)
302    def __init__(
303        self,
304        environment: "Environment",
305        name: t.Optional[str],
306        filename: t.Optional[str],
307        stream: t.Optional[t.TextIO] = None,
308        defer_init: bool = False,
309        optimized: bool = True,
310    ) -> None:
311        if stream is None:
312            stream = StringIO()
313        self.environment = environment
314        self.name = name
315        self.filename = filename
316        self.stream = stream
317        self.created_block_context = False
318        self.defer_init = defer_init
319        self.optimizer: t.Optional[Optimizer] = None
320
321        if optimized:
322            self.optimizer = Optimizer(environment)
323
324        # aliases for imports
325        self.import_aliases: t.Dict[str, str] = {}
326
327        # a registry for all blocks.  Because blocks are moved out
328        # into the global python scope they are registered here
329        self.blocks: t.Dict[str, nodes.Block] = {}
330
331        # the number of extends statements so far
332        self.extends_so_far = 0
333
334        # some templates have a rootlevel extends.  In this case we
335        # can safely assume that we're a child template and do some
336        # more optimizations.
337        self.has_known_extends = False
338
339        # the current line number
340        self.code_lineno = 1
341
342        # registry of all filters and tests (global, not block local)
343        self.tests: t.Dict[str, str] = {}
344        self.filters: t.Dict[str, str] = {}
345
346        # the debug information
347        self.debug_info: t.List[t.Tuple[int, int]] = []
348        self._write_debug_info: t.Optional[int] = None
349
350        # the number of new lines before the next write()
351        self._new_lines = 0
352
353        # the line number of the last written statement
354        self._last_line = 0
355
356        # true if nothing was written so far.
357        self._first_write = True
358
359        # used by the `temporary_identifier` method to get new
360        # unique, temporary identifier
361        self._last_identifier = 0
362
363        # the current indentation
364        self._indentation = 0
365
366        # Tracks toplevel assignments
367        self._assign_stack: t.List[t.Set[str]] = []
368
369        # Tracks parameter definition blocks
370        self._param_def_block: t.List[t.Set[str]] = []
371
372        # Tracks the current context.
373        self._context_reference_stack = ["context"]
environment
name
filename
stream
created_block_context
defer_init
optimizer: Optional[jinja2.optimizer.Optimizer]
import_aliases: Dict[str, str]
blocks: Dict[str, jinja2.nodes.Block]
extends_so_far
has_known_extends
code_lineno
tests: Dict[str, str]
filters: Dict[str, str]
debug_info: List[Tuple[int, int]]
optimized: bool
375    @property
376    def optimized(self) -> bool:
377        return self.optimizer is not None
def fail(self, msg: str, lineno: int) -> NoReturn:
381    def fail(self, msg: str, lineno: int) -> "te.NoReturn":
382        """Fail with a :exc:`TemplateAssertionError`."""
383        raise TemplateAssertionError(msg, lineno, self.name, self.filename)

Fail with a TemplateAssertionError.

def temporary_identifier(self) -> str:
385    def temporary_identifier(self) -> str:
386        """Get a new unique identifier."""
387        self._last_identifier += 1
388        return f"t_{self._last_identifier}"

Get a new unique identifier.

def buffer(self, frame: Frame) -> None:
390    def buffer(self, frame: Frame) -> None:
391        """Enable buffering for the frame from that point onwards."""
392        frame.buffer = self.temporary_identifier()
393        self.writeline(f"{frame.buffer} = []")

Enable buffering for the frame from that point onwards.

def return_buffer_contents( self, frame: Frame, force_unescaped: bool = False) -> None:
395    def return_buffer_contents(
396        self, frame: Frame, force_unescaped: bool = False
397    ) -> None:
398        """Return the buffer contents of the frame."""
399        if not force_unescaped:
400            if frame.eval_ctx.volatile:
401                self.writeline("if context.eval_ctx.autoescape:")
402                self.indent()
403                self.writeline(f"return Markup(concat({frame.buffer}))")
404                self.outdent()
405                self.writeline("else:")
406                self.indent()
407                self.writeline(f"return concat({frame.buffer})")
408                self.outdent()
409                return
410            elif frame.eval_ctx.autoescape:
411                self.writeline(f"return Markup(concat({frame.buffer}))")
412                return
413        self.writeline(f"return concat({frame.buffer})")

Return the buffer contents of the frame.

def indent(self) -> None:
415    def indent(self) -> None:
416        """Indent by one."""
417        self._indentation += 1

Indent by one.

def outdent(self, step: int = 1) -> None:
419    def outdent(self, step: int = 1) -> None:
420        """Outdent by step."""
421        self._indentation -= step

Outdent by step.

def start_write( self, frame: Frame, node: Optional[jinja2.nodes.Node] = None) -> None:
423    def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
424        """Yield or write into the frame buffer."""
425        if frame.buffer is None:
426            self.writeline("yield ", node)
427        else:
428            self.writeline(f"{frame.buffer}.append(", node)

Yield or write into the frame buffer.

def end_write(self, frame: Frame) -> None:
430    def end_write(self, frame: Frame) -> None:
431        """End the writing process started by `start_write`."""
432        if frame.buffer is not None:
433            self.write(")")

End the writing process started by start_write.

def simple_write( self, s: str, frame: Frame, node: Optional[jinja2.nodes.Node] = None) -> None:
435    def simple_write(
436        self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
437    ) -> None:
438        """Simple shortcut for start_write + write + end_write."""
439        self.start_write(frame, node)
440        self.write(s)
441        self.end_write(frame)

Simple shortcut for start_write + write + end_write.

def blockvisit( self, nodes: Iterable[jinja2.nodes.Node], frame: Frame) -> None:
443    def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
444        """Visit a list of nodes as block in a frame.  If the current frame
445        is no buffer a dummy ``if 0: yield None`` is written automatically.
446        """
447        try:
448            self.writeline("pass")
449            for node in nodes:
450                self.visit(node, frame)
451        except CompilerExit:
452            pass

Visit a list of nodes as block in a frame. If the current frame is no buffer a dummy if 0: yield None is written automatically.

def write(self, x: str) -> None:
454    def write(self, x: str) -> None:
455        """Write a string into the output stream."""
456        if self._new_lines:
457            if not self._first_write:
458                self.stream.write("\n" * self._new_lines)
459                self.code_lineno += self._new_lines
460                if self._write_debug_info is not None:
461                    self.debug_info.append((self._write_debug_info, self.code_lineno))
462                    self._write_debug_info = None
463            self._first_write = False
464            self.stream.write("    " * self._indentation)
465            self._new_lines = 0
466        self.stream.write(x)

Write a string into the output stream.

def writeline( self, x: str, node: Optional[jinja2.nodes.Node] = None, extra: int = 0) -> None:
468    def writeline(
469        self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
470    ) -> None:
471        """Combination of newline and write."""
472        self.newline(node, extra)
473        self.write(x)

Combination of newline and write.

def newline(self, node: Optional[jinja2.nodes.Node] = None, extra: int = 0) -> None:
475    def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
476        """Add one or more newlines before the next write."""
477        self._new_lines = max(self._new_lines, 1 + extra)
478        if node is not None and node.lineno != self._last_line:
479            self._write_debug_info = node.lineno
480            self._last_line = node.lineno

Add one or more newlines before the next write.

def signature( self, node: Union[jinja2.nodes.Call, jinja2.nodes.Filter, jinja2.nodes.Test], frame: Frame, extra_kwargs: Optional[Mapping[str, Any]] = None) -> None:
482    def signature(
483        self,
484        node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
485        frame: Frame,
486        extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
487    ) -> None:
488        """Writes a function call to the stream for the current node.
489        A leading comma is added automatically.  The extra keyword
490        arguments may not include python keywords otherwise a syntax
491        error could occur.  The extra keyword arguments should be given
492        as python dict.
493        """
494        # if any of the given keyword arguments is a python keyword
495        # we have to make sure that no invalid call is created.
496        kwarg_workaround = any(
497            is_python_keyword(t.cast(str, k))
498            for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
499        )
500
501        for arg in node.args:
502            self.write(", ")
503            self.visit(arg, frame)
504
505        if not kwarg_workaround:
506            for kwarg in node.kwargs:
507                self.write(", ")
508                self.visit(kwarg, frame)
509            if extra_kwargs is not None:
510                for key, value in extra_kwargs.items():
511                    self.write(f", {key}={value}")
512        if node.dyn_args:
513            self.write(", *")
514            self.visit(node.dyn_args, frame)
515
516        if kwarg_workaround:
517            if node.dyn_kwargs is not None:
518                self.write(", **dict({")
519            else:
520                self.write(", **{")
521            for kwarg in node.kwargs:
522                self.write(f"{kwarg.key!r}: ")
523                self.visit(kwarg.value, frame)
524                self.write(", ")
525            if extra_kwargs is not None:
526                for key, value in extra_kwargs.items():
527                    self.write(f"{key!r}: {value}, ")
528            if node.dyn_kwargs is not None:
529                self.write("}, **")
530                self.visit(node.dyn_kwargs, frame)
531                self.write(")")
532            else:
533                self.write("}")
534
535        elif node.dyn_kwargs is not None:
536            self.write(", **")
537            self.visit(node.dyn_kwargs, frame)

Writes a function call to the stream for the current node. A leading comma is added automatically. The extra keyword arguments may not include python keywords otherwise a syntax error could occur. The extra keyword arguments should be given as python dict.

def pull_dependencies(self, nodes: Iterable[jinja2.nodes.Node]) -> None:
539    def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
540        """Find all filter and test names used in the template and
541        assign them to variables in the compiled namespace. Checking
542        that the names are registered with the environment is done when
543        compiling the Filter and Test nodes. If the node is in an If or
544        CondExpr node, the check is done at runtime instead.
545
546        .. versionchanged:: 3.0
547            Filters and tests in If and CondExpr nodes are checked at
548            runtime instead of compile time.
549        """
550        visitor = DependencyFinderVisitor()
551
552        for node in nodes:
553            visitor.visit(node)
554
555        for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
556            self.tests,
557            visitor.tests,
558            "tests",
559        ):
560            for name in sorted(names):
561                if name not in id_map:
562                    id_map[name] = self.temporary_identifier()
563
564                # add check during runtime that dependencies used inside of executed
565                # blocks are defined, as this step may be skipped during compile time
566                self.writeline("try:")
567                self.indent()
568                self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
569                self.outdent()
570                self.writeline("except KeyError:")
571                self.indent()
572                self.writeline("@internalcode")
573                self.writeline(f"def {id_map[name]}(*unused):")
574                self.indent()
575                self.writeline(
576                    f'raise TemplateRuntimeError("No {dependency[:-1]}'
577                    f' named {name!r} found.")'
578                )
579                self.outdent()
580                self.outdent()

Find all filter and test names used in the template and assign them to variables in the compiled namespace. Checking that the names are registered with the environment is done when compiling the Filter and Test nodes. If the node is in an If or CondExpr node, the check is done at runtime instead.

Changed in version 3.0: Filters and tests in If and CondExpr nodes are checked at runtime instead of compile time.

def enter_frame(self, frame: Frame) -> None:
582    def enter_frame(self, frame: Frame) -> None:
583        undefs = []
584        for target, (action, param) in frame.symbols.loads.items():
585            if action == VAR_LOAD_PARAMETER:
586                pass
587            elif action == VAR_LOAD_RESOLVE:
588                self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
589            elif action == VAR_LOAD_ALIAS:
590                self.writeline(f"{target} = {param}")
591            elif action == VAR_LOAD_UNDEFINED:
592                undefs.append(target)
593            else:
594                raise NotImplementedError("unknown load instruction")
595        if undefs:
596            self.writeline(f"{' = '.join(undefs)} = missing")
def leave_frame( self, frame: Frame, with_python_scope: bool = False) -> None:
598    def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
599        if not with_python_scope:
600            undefs = []
601            for target in frame.symbols.loads:
602                undefs.append(target)
603            if undefs:
604                self.writeline(f"{' = '.join(undefs)} = missing")
def choose_async(self, async_value: str = 'async ', sync_value: str = '') -> str:
606    def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
607        return async_value if self.environment.is_async else sync_value
def func(self, name: str) -> str:
609    def func(self, name: str) -> str:
610        return f"{self.choose_async()}def {name}"
def macro_body( self, node: Union[jinja2.nodes.Macro, jinja2.nodes.CallBlock], frame: Frame) -> Tuple[Frame, MacroRef]:
612    def macro_body(
613        self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
614    ) -> t.Tuple[Frame, MacroRef]:
615        """Dump the function def of a macro or call block."""
616        frame = frame.inner()
617        frame.symbols.analyze_node(node)
618        macro_ref = MacroRef(node)
619
620        explicit_caller = None
621        skip_special_params = set()
622        args = []
623
624        for idx, arg in enumerate(node.args):
625            if arg.name == "caller":
626                explicit_caller = idx
627            if arg.name in ("kwargs", "varargs"):
628                skip_special_params.add(arg.name)
629            args.append(frame.symbols.ref(arg.name))
630
631        undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
632
633        if "caller" in undeclared:
634            # In older Jinja versions there was a bug that allowed caller
635            # to retain the special behavior even if it was mentioned in
636            # the argument list.  However thankfully this was only really
637            # working if it was the last argument.  So we are explicitly
638            # checking this now and error out if it is anywhere else in
639            # the argument list.
640            if explicit_caller is not None:
641                try:
642                    node.defaults[explicit_caller - len(node.args)]
643                except IndexError:
644                    self.fail(
645                        "When defining macros or call blocks the "
646                        'special "caller" argument must be omitted '
647                        "or be given a default.",
648                        node.lineno,
649                    )
650            else:
651                args.append(frame.symbols.declare_parameter("caller"))
652            macro_ref.accesses_caller = True
653        if "kwargs" in undeclared and "kwargs" not in skip_special_params:
654            args.append(frame.symbols.declare_parameter("kwargs"))
655            macro_ref.accesses_kwargs = True
656        if "varargs" in undeclared and "varargs" not in skip_special_params:
657            args.append(frame.symbols.declare_parameter("varargs"))
658            macro_ref.accesses_varargs = True
659
660        # macros are delayed, they never require output checks
661        frame.require_output_check = False
662        frame.symbols.analyze_node(node)
663        self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
664        self.indent()
665
666        self.buffer(frame)
667        self.enter_frame(frame)
668
669        self.push_parameter_definitions(frame)
670        for idx, arg in enumerate(node.args):
671            ref = frame.symbols.ref(arg.name)
672            self.writeline(f"if {ref} is missing:")
673            self.indent()
674            try:
675                default = node.defaults[idx - len(node.args)]
676            except IndexError:
677                self.writeline(
678                    f'{ref} = undefined("parameter {arg.name!r} was not provided",'
679                    f" name={arg.name!r})"
680                )
681            else:
682                self.writeline(f"{ref} = ")
683                self.visit(default, frame)
684            self.mark_parameter_stored(ref)
685            self.outdent()
686        self.pop_parameter_definitions()
687
688        self.blockvisit(node.body, frame)
689        self.return_buffer_contents(frame, force_unescaped=True)
690        self.leave_frame(frame, with_python_scope=True)
691        self.outdent()
692
693        return frame, macro_ref

Dump the function def of a macro or call block.

def macro_def( self, macro_ref: MacroRef, frame: Frame) -> None:
695    def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
696        """Dump the macro definition for the def created by macro_body."""
697        arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
698        name = getattr(macro_ref.node, "name", None)
699        if len(macro_ref.node.args) == 1:
700            arg_tuple += ","
701        self.write(
702            f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
703            f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
704            f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
705        )

Dump the macro definition for the def created by macro_body.

def position(self, node: jinja2.nodes.Node) -> str:
707    def position(self, node: nodes.Node) -> str:
708        """Return a human readable position for the node."""
709        rv = f"line {node.lineno}"
710        if self.name is not None:
711            rv = f"{rv} in {self.name!r}"
712        return rv

Return a human readable position for the node.

def dump_local_context(self, frame: Frame) -> str:
714    def dump_local_context(self, frame: Frame) -> str:
715        items_kv = ", ".join(
716            f"{name!r}: {target}"
717            for name, target in frame.symbols.dump_stores().items()
718        )
719        return f"{{{items_kv}}}"
def write_commons(self) -> None:
721    def write_commons(self) -> None:
722        """Writes a common preamble that is used by root and block functions.
723        Primarily this sets up common local helpers and enforces a generator
724        through a dead branch.
725        """
726        self.writeline("resolve = context.resolve_or_missing")
727        self.writeline("undefined = environment.undefined")
728        self.writeline("concat = environment.concat")
729        # always use the standard Undefined class for the implicit else of
730        # conditional expressions
731        self.writeline("cond_expr_undefined = Undefined")
732        self.writeline("if 0: yield None")

Writes a common preamble that is used by root and block functions. Primarily this sets up common local helpers and enforces a generator through a dead branch.

def push_parameter_definitions(self, frame: Frame) -> None:
734    def push_parameter_definitions(self, frame: Frame) -> None:
735        """Pushes all parameter targets from the given frame into a local
736        stack that permits tracking of yet to be assigned parameters.  In
737        particular this enables the optimization from `visit_Name` to skip
738        undefined expressions for parameters in macros as macros can reference
739        otherwise unbound parameters.
740        """
741        self._param_def_block.append(frame.symbols.dump_param_targets())

Pushes all parameter targets from the given frame into a local stack that permits tracking of yet to be assigned parameters. In particular this enables the optimization from visit_Name to skip undefined expressions for parameters in macros as macros can reference otherwise unbound parameters.

def pop_parameter_definitions(self) -> None:
743    def pop_parameter_definitions(self) -> None:
744        """Pops the current parameter definitions set."""
745        self._param_def_block.pop()

Pops the current parameter definitions set.

def mark_parameter_stored(self, target: str) -> None:
747    def mark_parameter_stored(self, target: str) -> None:
748        """Marks a parameter in the current parameter definitions as stored.
749        This will skip the enforced undefined checks.
750        """
751        if self._param_def_block:
752            self._param_def_block[-1].discard(target)

Marks a parameter in the current parameter definitions as stored. This will skip the enforced undefined checks.

def push_context_reference(self, target: str) -> None:
754    def push_context_reference(self, target: str) -> None:
755        self._context_reference_stack.append(target)
def pop_context_reference(self) -> None:
757    def pop_context_reference(self) -> None:
758        self._context_reference_stack.pop()
def get_context_ref(self) -> str:
760    def get_context_ref(self) -> str:
761        return self._context_reference_stack[-1]
def get_resolve_func(self) -> str:
763    def get_resolve_func(self) -> str:
764        target = self._context_reference_stack[-1]
765        if target == "context":
766            return "resolve"
767        return f"{target}.resolve"
def derive_context(self, frame: Frame) -> str:
769    def derive_context(self, frame: Frame) -> str:
770        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
def parameter_is_undeclared(self, target: str) -> bool:
772    def parameter_is_undeclared(self, target: str) -> bool:
773        """Checks if a given target is an undeclared parameter."""
774        if not self._param_def_block:
775            return False
776        return target in self._param_def_block[-1]

Checks if a given target is an undeclared parameter.

def push_assign_tracking(self) -> None:
778    def push_assign_tracking(self) -> None:
779        """Pushes a new layer for assignment tracking."""
780        self._assign_stack.append(set())

Pushes a new layer for assignment tracking.

def pop_assign_tracking(self, frame: Frame) -> None:
782    def pop_assign_tracking(self, frame: Frame) -> None:
783        """Pops the topmost level for assignment tracking and updates the
784        context variables if necessary.
785        """
786        vars = self._assign_stack.pop()
787        if (
788            not frame.block_frame
789            and not frame.loop_frame
790            and not frame.toplevel
791            or not vars
792        ):
793            return
794        public_names = [x for x in vars if x[:1] != "_"]
795        if len(vars) == 1:
796            name = next(iter(vars))
797            ref = frame.symbols.ref(name)
798            if frame.loop_frame:
799                self.writeline(f"_loop_vars[{name!r}] = {ref}")
800                return
801            if frame.block_frame:
802                self.writeline(f"_block_vars[{name!r}] = {ref}")
803                return
804            self.writeline(f"context.vars[{name!r}] = {ref}")
805        else:
806            if frame.loop_frame:
807                self.writeline("_loop_vars.update({")
808            elif frame.block_frame:
809                self.writeline("_block_vars.update({")
810            else:
811                self.writeline("context.vars.update({")
812            for idx, name in enumerate(vars):
813                if idx:
814                    self.write(", ")
815                ref = frame.symbols.ref(name)
816                self.write(f"{name!r}: {ref}")
817            self.write("})")
818        if not frame.block_frame and not frame.loop_frame and public_names:
819            if len(public_names) == 1:
820                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
821            else:
822                names_str = ", ".join(map(repr, public_names))
823                self.writeline(f"context.exported_vars.update(({names_str}))")

Pops the topmost level for assignment tracking and updates the context variables if necessary.

def visit_Template( self, node: jinja2.nodes.Template, frame: Optional[Frame] = None) -> None:
827    def visit_Template(
828        self, node: nodes.Template, frame: t.Optional[Frame] = None
829    ) -> None:
830        assert frame is None, "no root frame allowed"
831        eval_ctx = EvalContext(self.environment, self.name)
832
833        from .runtime import exported, async_exported
834
835        if self.environment.is_async:
836            exported_names = sorted(exported + async_exported)
837        else:
838            exported_names = sorted(exported)
839
840        self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
841
842        # if we want a deferred initialization we cannot move the
843        # environment into a local name
844        envenv = "" if self.defer_init else ", environment=environment"
845
846        # do we have an extends tag at all?  If not, we can save some
847        # overhead by just not processing any inheritance code.
848        have_extends = node.find(nodes.Extends) is not None
849
850        # find all blocks
851        for block in node.find_all(nodes.Block):
852            if block.name in self.blocks:
853                self.fail(f"block {block.name!r} defined twice", block.lineno)
854            self.blocks[block.name] = block
855
856        # find all imports and import them
857        for import_ in node.find_all(nodes.ImportedName):
858            if import_.importname not in self.import_aliases:
859                imp = import_.importname
860                self.import_aliases[imp] = alias = self.temporary_identifier()
861                if "." in imp:
862                    module, obj = imp.rsplit(".", 1)
863                    self.writeline(f"from {module} import {obj} as {alias}")
864                else:
865                    self.writeline(f"import {imp} as {alias}")
866
867        # add the load name
868        self.writeline(f"name = {self.name!r}")
869
870        # generate the root render function.
871        self.writeline(
872            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
873        )
874        self.indent()
875        self.write_commons()
876
877        # process the root
878        frame = Frame(eval_ctx)
879        if "self" in find_undeclared(node.body, ("self",)):
880            ref = frame.symbols.declare_parameter("self")
881            self.writeline(f"{ref} = TemplateReference(context)")
882        frame.symbols.analyze_node(node)
883        frame.toplevel = frame.rootlevel = True
884        frame.require_output_check = have_extends and not self.has_known_extends
885        if have_extends:
886            self.writeline("parent_template = None")
887        self.enter_frame(frame)
888        self.pull_dependencies(node.body)
889        self.blockvisit(node.body, frame)
890        self.leave_frame(frame, with_python_scope=True)
891        self.outdent()
892
893        # make sure that the parent root is called.
894        if have_extends:
895            if not self.has_known_extends:
896                self.indent()
897                self.writeline("if parent_template is not None:")
898            self.indent()
899            if not self.environment.is_async:
900                self.writeline("yield from parent_template.root_render_func(context)")
901            else:
902                self.writeline(
903                    "async for event in parent_template.root_render_func(context):"
904                )
905                self.indent()
906                self.writeline("yield event")
907                self.outdent()
908            self.outdent(1 + (not self.has_known_extends))
909
910        # at this point we now have the blocks collected and can visit them too.
911        for name, block in self.blocks.items():
912            self.writeline(
913                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
914                block,
915                1,
916            )
917            self.indent()
918            self.write_commons()
919            # It's important that we do not make this frame a child of the
920            # toplevel template.  This would cause a variety of
921            # interesting issues with identifier tracking.
922            block_frame = Frame(eval_ctx)
923            block_frame.block_frame = True
924            undeclared = find_undeclared(block.body, ("self", "super"))
925            if "self" in undeclared:
926                ref = block_frame.symbols.declare_parameter("self")
927                self.writeline(f"{ref} = TemplateReference(context)")
928            if "super" in undeclared:
929                ref = block_frame.symbols.declare_parameter("super")
930                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
931            block_frame.symbols.analyze_node(block)
932            block_frame.block = name
933            self.writeline("_block_vars = {}")
934            self.enter_frame(block_frame)
935            self.pull_dependencies(block.body)
936            self.blockvisit(block.body, block_frame)
937            self.leave_frame(block_frame, with_python_scope=True)
938            self.outdent()
939
940        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
941        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
942        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
943        self.writeline(f"debug_info = {debug_kv_str!r}")
def visit_Block(self, node: jinja2.nodes.Block, frame: Frame) -> None:
945    def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
946        """Call a block and register it for the template."""
947        level = 0
948        if frame.toplevel:
949            # if we know that we are a child template, there is no need to
950            # check if we are one
951            if self.has_known_extends:
952                return
953            if self.extends_so_far > 0:
954                self.writeline("if parent_template is None:")
955                self.indent()
956                level += 1
957
958        if node.scoped:
959            context = self.derive_context(frame)
960        else:
961            context = self.get_context_ref()
962
963        if node.required:
964            self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
965            self.indent()
966            self.writeline(
967                f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
968                node,
969            )
970            self.outdent()
971
972        if not self.environment.is_async and frame.buffer is None:
973            self.writeline(
974                f"yield from context.blocks[{node.name!r}][0]({context})", node
975            )
976        else:
977            self.writeline(
978                f"{self.choose_async()}for event in"
979                f" context.blocks[{node.name!r}][0]({context}):",
980                node,
981            )
982            self.indent()
983            self.simple_write("event", frame)
984            self.outdent()
985
986        self.outdent(level)

Call a block and register it for the template.

def visit_Extends(self, node: jinja2.nodes.Extends, frame: Frame) -> None:
 988    def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
 989        """Calls the extender."""
 990        if not frame.toplevel:
 991            self.fail("cannot use extend from a non top-level scope", node.lineno)
 992
 993        # if the number of extends statements in general is zero so
 994        # far, we don't have to add a check if something extended
 995        # the template before this one.
 996        if self.extends_so_far > 0:
 997            # if we have a known extends we just add a template runtime
 998            # error into the generated code.  We could catch that at compile
 999            # time too, but i welcome it not to confuse users by throwing the
1000            # same error at different times just "because we can".
1001            if not self.has_known_extends:
1002                self.writeline("if parent_template is not None:")
1003                self.indent()
1004            self.writeline('raise TemplateRuntimeError("extended multiple times")')
1005
1006            # if we have a known extends already we don't need that code here
1007            # as we know that the template execution will end here.
1008            if self.has_known_extends:
1009                raise CompilerExit()
1010            else:
1011                self.outdent()
1012
1013        self.writeline("parent_template = environment.get_template(", node)
1014        self.visit(node.template, frame)
1015        self.write(f", {self.name!r})")
1016        self.writeline("for name, parent_block in parent_template.blocks.items():")
1017        self.indent()
1018        self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
1019        self.outdent()
1020
1021        # if this extends statement was in the root level we can take
1022        # advantage of that information and simplify the generated code
1023        # in the top level from this point onwards
1024        if frame.rootlevel:
1025            self.has_known_extends = True
1026
1027        # and now we have one more
1028        self.extends_so_far += 1

Calls the extender.

def visit_Include(self, node: jinja2.nodes.Include, frame: Frame) -> None:
1030    def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1031        """Handles includes."""
1032        if node.ignore_missing:
1033            self.writeline("try:")
1034            self.indent()
1035
1036        func_name = "get_or_select_template"
1037        if isinstance(node.template, nodes.Const):
1038            if isinstance(node.template.value, str):
1039                func_name = "get_template"
1040            elif isinstance(node.template.value, (tuple, list)):
1041                func_name = "select_template"
1042        elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1043            func_name = "select_template"
1044
1045        self.writeline(f"template = environment.{func_name}(", node)
1046        self.visit(node.template, frame)
1047        self.write(f", {self.name!r})")
1048        if node.ignore_missing:
1049            self.outdent()
1050            self.writeline("except TemplateNotFound:")
1051            self.indent()
1052            self.writeline("pass")
1053            self.outdent()
1054            self.writeline("else:")
1055            self.indent()
1056
1057        skip_event_yield = False
1058        if node.with_context:
1059            self.writeline(
1060                f"{self.choose_async()}for event in template.root_render_func("
1061                "template.new_context(context.get_all(), True,"
1062                f" {self.dump_local_context(frame)})):"
1063            )
1064        elif self.environment.is_async:
1065            self.writeline(
1066                "for event in (await template._get_default_module_async())"
1067                "._body_stream:"
1068            )
1069        else:
1070            self.writeline("yield from template._get_default_module()._body_stream")
1071            skip_event_yield = True
1072
1073        if not skip_event_yield:
1074            self.indent()
1075            self.simple_write("event", frame)
1076            self.outdent()
1077
1078        if node.ignore_missing:
1079            self.outdent()

Handles includes.

def visit_Import(self, node: jinja2.nodes.Import, frame: Frame) -> None:
1096    def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1097        """Visit regular imports."""
1098        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1099        if frame.toplevel:
1100            self.write(f"context.vars[{node.target!r}] = ")
1101
1102        self._import_common(node, frame)
1103
1104        if frame.toplevel and not node.target.startswith("_"):
1105            self.writeline(f"context.exported_vars.discard({node.target!r})")

Visit regular imports.

def visit_FromImport( self, node: jinja2.nodes.FromImport, frame: Frame) -> None:
1107    def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1108        """Visit named imports."""
1109        self.newline(node)
1110        self.write("included_template = ")
1111        self._import_common(node, frame)
1112        var_names = []
1113        discarded_names = []
1114        for name in node.names:
1115            if isinstance(name, tuple):
1116                name, alias = name
1117            else:
1118                alias = name
1119            self.writeline(
1120                f"{frame.symbols.ref(alias)} ="
1121                f" getattr(included_template, {name!r}, missing)"
1122            )
1123            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1124            self.indent()
1125            message = (
1126                "the template {included_template.__name__!r}"
1127                f" (imported on {self.position(node)})"
1128                f" does not export the requested name {name!r}"
1129            )
1130            self.writeline(
1131                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1132            )
1133            self.outdent()
1134            if frame.toplevel:
1135                var_names.append(alias)
1136                if not alias.startswith("_"):
1137                    discarded_names.append(alias)
1138
1139        if var_names:
1140            if len(var_names) == 1:
1141                name = var_names[0]
1142                self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1143            else:
1144                names_kv = ", ".join(
1145                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1146                )
1147                self.writeline(f"context.vars.update({{{names_kv}}})")
1148        if discarded_names:
1149            if len(discarded_names) == 1:
1150                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1151            else:
1152                names_str = ", ".join(map(repr, discarded_names))
1153                self.writeline(
1154                    f"context.exported_vars.difference_update(({names_str}))"
1155                )

Visit named imports.

def visit_For(self, node: jinja2.nodes.For, frame: Frame) -> None:
1157    def visit_For(self, node: nodes.For, frame: Frame) -> None:
1158        loop_frame = frame.inner()
1159        loop_frame.loop_frame = True
1160        test_frame = frame.inner()
1161        else_frame = frame.inner()
1162
1163        # try to figure out if we have an extended loop.  An extended loop
1164        # is necessary if the loop is in recursive mode if the special loop
1165        # variable is accessed in the body if the body is a scoped block.
1166        extended_loop = (
1167            node.recursive
1168            or "loop"
1169            in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1170            or any(block.scoped for block in node.find_all(nodes.Block))
1171        )
1172
1173        loop_ref = None
1174        if extended_loop:
1175            loop_ref = loop_frame.symbols.declare_parameter("loop")
1176
1177        loop_frame.symbols.analyze_node(node, for_branch="body")
1178        if node.else_:
1179            else_frame.symbols.analyze_node(node, for_branch="else")
1180
1181        if node.test:
1182            loop_filter_func = self.temporary_identifier()
1183            test_frame.symbols.analyze_node(node, for_branch="test")
1184            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1185            self.indent()
1186            self.enter_frame(test_frame)
1187            self.writeline(self.choose_async("async for ", "for "))
1188            self.visit(node.target, loop_frame)
1189            self.write(" in ")
1190            self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1191            self.write(":")
1192            self.indent()
1193            self.writeline("if ", node.test)
1194            self.visit(node.test, test_frame)
1195            self.write(":")
1196            self.indent()
1197            self.writeline("yield ")
1198            self.visit(node.target, loop_frame)
1199            self.outdent(3)
1200            self.leave_frame(test_frame, with_python_scope=True)
1201
1202        # if we don't have an recursive loop we have to find the shadowed
1203        # variables at that point.  Because loops can be nested but the loop
1204        # variable is a special one we have to enforce aliasing for it.
1205        if node.recursive:
1206            self.writeline(
1207                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1208            )
1209            self.indent()
1210            self.buffer(loop_frame)
1211
1212            # Use the same buffer for the else frame
1213            else_frame.buffer = loop_frame.buffer
1214
1215        # make sure the loop variable is a special one and raise a template
1216        # assertion error if a loop tries to write to loop
1217        if extended_loop:
1218            self.writeline(f"{loop_ref} = missing")
1219
1220        for name in node.find_all(nodes.Name):
1221            if name.ctx == "store" and name.name == "loop":
1222                self.fail(
1223                    "Can't assign to special loop variable in for-loop target",
1224                    name.lineno,
1225                )
1226
1227        if node.else_:
1228            iteration_indicator = self.temporary_identifier()
1229            self.writeline(f"{iteration_indicator} = 1")
1230
1231        self.writeline(self.choose_async("async for ", "for "), node)
1232        self.visit(node.target, loop_frame)
1233        if extended_loop:
1234            self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1235        else:
1236            self.write(" in ")
1237
1238        if node.test:
1239            self.write(f"{loop_filter_func}(")
1240        if node.recursive:
1241            self.write("reciter")
1242        else:
1243            if self.environment.is_async and not extended_loop:
1244                self.write("auto_aiter(")
1245            self.visit(node.iter, frame)
1246            if self.environment.is_async and not extended_loop:
1247                self.write(")")
1248        if node.test:
1249            self.write(")")
1250
1251        if node.recursive:
1252            self.write(", undefined, loop_render_func, depth):")
1253        else:
1254            self.write(", undefined):" if extended_loop else ":")
1255
1256        self.indent()
1257        self.enter_frame(loop_frame)
1258
1259        self.writeline("_loop_vars = {}")
1260        self.blockvisit(node.body, loop_frame)
1261        if node.else_:
1262            self.writeline(f"{iteration_indicator} = 0")
1263        self.outdent()
1264        self.leave_frame(
1265            loop_frame, with_python_scope=node.recursive and not node.else_
1266        )
1267
1268        if node.else_:
1269            self.writeline(f"if {iteration_indicator}:")
1270            self.indent()
1271            self.enter_frame(else_frame)
1272            self.blockvisit(node.else_, else_frame)
1273            self.leave_frame(else_frame)
1274            self.outdent()
1275
1276        # if the node was recursive we have to return the buffer contents
1277        # and start the iteration code
1278        if node.recursive:
1279            self.return_buffer_contents(loop_frame)
1280            self.outdent()
1281            self.start_write(frame, node)
1282            self.write(f"{self.choose_async('await ')}loop(")
1283            if self.environment.is_async:
1284                self.write("auto_aiter(")
1285            self.visit(node.iter, frame)
1286            if self.environment.is_async:
1287                self.write(")")
1288            self.write(", loop)")
1289            self.end_write(frame)
1290
1291        # at the end of the iteration, clear any assignments made in the
1292        # loop from the top level
1293        if self._assign_stack:
1294            self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
def visit_If(self, node: jinja2.nodes.If, frame: Frame) -> None:
1296    def visit_If(self, node: nodes.If, frame: Frame) -> None:
1297        if_frame = frame.soft()
1298        self.writeline("if ", node)
1299        self.visit(node.test, if_frame)
1300        self.write(":")
1301        self.indent()
1302        self.blockvisit(node.body, if_frame)
1303        self.outdent()
1304        for elif_ in node.elif_:
1305            self.writeline("elif ", elif_)
1306            self.visit(elif_.test, if_frame)
1307            self.write(":")
1308            self.indent()
1309            self.blockvisit(elif_.body, if_frame)
1310            self.outdent()
1311        if node.else_:
1312            self.writeline("else:")
1313            self.indent()
1314            self.blockvisit(node.else_, if_frame)
1315            self.outdent()
def visit_Macro(self, node: jinja2.nodes.Macro, frame: Frame) -> None:
1317    def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1318        macro_frame, macro_ref = self.macro_body(node, frame)
1319        self.newline()
1320        if frame.toplevel:
1321            if not node.name.startswith("_"):
1322                self.write(f"context.exported_vars.add({node.name!r})")
1323            self.writeline(f"context.vars[{node.name!r}] = ")
1324        self.write(f"{frame.symbols.ref(node.name)} = ")
1325        self.macro_def(macro_ref, macro_frame)
def visit_CallBlock(self, node: jinja2.nodes.CallBlock, frame: Frame) -> None:
1327    def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1328        call_frame, macro_ref = self.macro_body(node, frame)
1329        self.writeline("caller = ")
1330        self.macro_def(macro_ref, call_frame)
1331        self.start_write(frame, node)
1332        self.visit_Call(node.call, frame, forward_caller=True)
1333        self.end_write(frame)
def visit_FilterBlock( self, node: jinja2.nodes.FilterBlock, frame: Frame) -> None:
1335    def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1336        filter_frame = frame.inner()
1337        filter_frame.symbols.analyze_node(node)
1338        self.enter_frame(filter_frame)
1339        self.buffer(filter_frame)
1340        self.blockvisit(node.body, filter_frame)
1341        self.start_write(frame, node)
1342        self.visit_Filter(node.filter, filter_frame)
1343        self.end_write(frame)
1344        self.leave_frame(filter_frame)
def visit_With(self, node: jinja2.nodes.With, frame: Frame) -> None:
1346    def visit_With(self, node: nodes.With, frame: Frame) -> None:
1347        with_frame = frame.inner()
1348        with_frame.symbols.analyze_node(node)
1349        self.enter_frame(with_frame)
1350        for target, expr in zip(node.targets, node.values):
1351            self.newline()
1352            self.visit(target, with_frame)
1353            self.write(" = ")
1354            self.visit(expr, frame)
1355        self.blockvisit(node.body, with_frame)
1356        self.leave_frame(with_frame)
def visit_ExprStmt(self, node: jinja2.nodes.ExprStmt, frame: Frame) -> None:
1358    def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1359        self.newline(node)
1360        self.visit(node.node, frame)
def visit_Output(self, node: jinja2.nodes.Output, frame: Frame) -> None:
1479    def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1480        # If an extends is active, don't render outside a block.
1481        if frame.require_output_check:
1482            # A top-level extends is known to exist at compile time.
1483            if self.has_known_extends:
1484                return
1485
1486            self.writeline("if parent_template is None:")
1487            self.indent()
1488
1489        finalize = self._make_finalize()
1490        body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1491
1492        # Evaluate constants at compile time if possible. Each item in
1493        # body will be either a list of static data or a node to be
1494        # evaluated at runtime.
1495        for child in node.nodes:
1496            try:
1497                if not (
1498                    # If the finalize function requires runtime context,
1499                    # constants can't be evaluated at compile time.
1500                    finalize.const
1501                    # Unless it's basic template data that won't be
1502                    # finalized anyway.
1503                    or isinstance(child, nodes.TemplateData)
1504                ):
1505                    raise nodes.Impossible()
1506
1507                const = self._output_child_to_const(child, frame, finalize)
1508            except (nodes.Impossible, Exception):
1509                # The node was not constant and needs to be evaluated at
1510                # runtime. Or another error was raised, which is easier
1511                # to debug at runtime.
1512                body.append(child)
1513                continue
1514
1515            if body and isinstance(body[-1], list):
1516                body[-1].append(const)
1517            else:
1518                body.append([const])
1519
1520        if frame.buffer is not None:
1521            if len(body) == 1:
1522                self.writeline(f"{frame.buffer}.append(")
1523            else:
1524                self.writeline(f"{frame.buffer}.extend((")
1525
1526            self.indent()
1527
1528        for item in body:
1529            if isinstance(item, list):
1530                # A group of constant data to join and output.
1531                val = self._output_const_repr(item)
1532
1533                if frame.buffer is None:
1534                    self.writeline("yield " + val)
1535                else:
1536                    self.writeline(val + ",")
1537            else:
1538                if frame.buffer is None:
1539                    self.writeline("yield ", item)
1540                else:
1541                    self.newline(item)
1542
1543                # A node to be evaluated at runtime.
1544                self._output_child_pre(item, frame, finalize)
1545                self.visit(item, frame)
1546                self._output_child_post(item, frame, finalize)
1547
1548                if frame.buffer is not None:
1549                    self.write(",")
1550
1551        if frame.buffer is not None:
1552            self.outdent()
1553            self.writeline(")" if len(body) == 1 else "))")
1554
1555        if frame.require_output_check:
1556            self.outdent()
def visit_Assign(self, node: jinja2.nodes.Assign, frame: Frame) -> None:
1558    def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1559        self.push_assign_tracking()
1560        self.newline(node)
1561        self.visit(node.target, frame)
1562        self.write(" = ")
1563        self.visit(node.node, frame)
1564        self.pop_assign_tracking(frame)
def visit_AssignBlock( self, node: jinja2.nodes.AssignBlock, frame: Frame) -> None:
1566    def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1567        self.push_assign_tracking()
1568        block_frame = frame.inner()
1569        # This is a special case.  Since a set block always captures we
1570        # will disable output checks.  This way one can use set blocks
1571        # toplevel even in extended templates.
1572        block_frame.require_output_check = False
1573        block_frame.symbols.analyze_node(node)
1574        self.enter_frame(block_frame)
1575        self.buffer(block_frame)
1576        self.blockvisit(node.body, block_frame)
1577        self.newline(node)
1578        self.visit(node.target, frame)
1579        self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1580        if node.filter is not None:
1581            self.visit_Filter(node.filter, block_frame)
1582        else:
1583            self.write(f"concat({block_frame.buffer})")
1584        self.write(")")
1585        self.pop_assign_tracking(frame)
1586        self.leave_frame(block_frame)
def visit_Name(self, node: jinja2.nodes.Name, frame: Frame) -> None:
1590    def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1591        if node.ctx == "store" and (
1592            frame.toplevel or frame.loop_frame or frame.block_frame
1593        ):
1594            if self._assign_stack:
1595                self._assign_stack[-1].add(node.name)
1596        ref = frame.symbols.ref(node.name)
1597
1598        # If we are looking up a variable we might have to deal with the
1599        # case where it's undefined.  We can skip that case if the load
1600        # instruction indicates a parameter which are always defined.
1601        if node.ctx == "load":
1602            load = frame.symbols.find_load(ref)
1603            if not (
1604                load is not None
1605                and load[0] == VAR_LOAD_PARAMETER
1606                and not self.parameter_is_undeclared(ref)
1607            ):
1608                self.write(
1609                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1610                )
1611                return
1612
1613        self.write(ref)
def visit_NSRef(self, node: jinja2.nodes.NSRef, frame: Frame) -> None:
1615    def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1616        # NSRefs can only be used to store values; since they use the normal
1617        # `foo.bar` notation they will be parsed as a normal attribute access
1618        # when used anywhere but in a `set` context
1619        ref = frame.symbols.ref(node.name)
1620        self.writeline(f"if not isinstance({ref}, Namespace):")
1621        self.indent()
1622        self.writeline(
1623            "raise TemplateRuntimeError"
1624            '("cannot assign attribute on non-namespace object")'
1625        )
1626        self.outdent()
1627        self.writeline(f"{ref}[{node.attr!r}]")
def visit_Const(self, node: jinja2.nodes.Const, frame: Frame) -> None:
1629    def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1630        val = node.as_const(frame.eval_ctx)
1631        if isinstance(val, float):
1632            self.write(str(val))
1633        else:
1634            self.write(repr(val))
def visit_TemplateData( self, node: jinja2.nodes.TemplateData, frame: Frame) -> None:
1636    def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1637        try:
1638            self.write(repr(node.as_const(frame.eval_ctx)))
1639        except nodes.Impossible:
1640            self.write(
1641                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1642            )
def visit_Tuple(self, node: jinja2.nodes.Tuple, frame: Frame) -> None:
1644    def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1645        self.write("(")
1646        idx = -1
1647        for idx, item in enumerate(node.items):
1648            if idx:
1649                self.write(", ")
1650            self.visit(item, frame)
1651        self.write(",)" if idx == 0 else ")")
def visit_List(self, node: jinja2.nodes.List, frame: Frame) -> None:
1653    def visit_List(self, node: nodes.List, frame: Frame) -> None:
1654        self.write("[")
1655        for idx, item in enumerate(node.items):
1656            if idx:
1657                self.write(", ")
1658            self.visit(item, frame)
1659        self.write("]")
def visit_Dict(self, node: jinja2.nodes.Dict, frame: Frame) -> None:
1661    def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1662        self.write("{")
1663        for idx, item in enumerate(node.items):
1664            if idx:
1665                self.write(", ")
1666            self.visit(item.key, frame)
1667            self.write(": ")
1668            self.visit(item.value, frame)
1669        self.write("}")
@optimizeconst
def visit_Add( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Sub( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Mul( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Div( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_FloorDiv( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Pow( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Mod( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_And( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Or( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
61    @optimizeconst
62    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
63        if (
64            self.environment.sandboxed
65            and op in self.environment.intercepted_binops  # type: ignore
66        ):
67            self.write(f"environment.call_binop(context, {op!r}, ")
68            self.visit(node.left, frame)
69            self.write(", ")
70            self.visit(node.right, frame)
71        else:
72            self.write("(")
73            self.visit(node.left, frame)
74            self.write(f" {op} ")
75            self.visit(node.right, frame)
76
77        self.write(")")
@optimizeconst
def visit_Pos( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
85    @optimizeconst
86    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
87        if (
88            self.environment.sandboxed
89            and op in self.environment.intercepted_unops  # type: ignore
90        ):
91            self.write(f"environment.call_unop(context, {op!r}, ")
92            self.visit(node.node, frame)
93        else:
94            self.write("(" + op)
95            self.visit(node.node, frame)
96
97        self.write(")")
@optimizeconst
def visit_Neg( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
85    @optimizeconst
86    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
87        if (
88            self.environment.sandboxed
89            and op in self.environment.intercepted_unops  # type: ignore
90        ):
91            self.write(f"environment.call_unop(context, {op!r}, ")
92            self.visit(node.node, frame)
93        else:
94            self.write("(" + op)
95            self.visit(node.node, frame)
96
97        self.write(")")
@optimizeconst
def visit_Not( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
85    @optimizeconst
86    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
87        if (
88            self.environment.sandboxed
89            and op in self.environment.intercepted_unops  # type: ignore
90        ):
91            self.write(f"environment.call_unop(context, {op!r}, ")
92            self.visit(node.node, frame)
93        else:
94            self.write("(" + op)
95            self.visit(node.node, frame)
96
97        self.write(")")
@optimizeconst
def visit_Concat(self, node: jinja2.nodes.Concat, frame: Frame) -> None:
1684    @optimizeconst
1685    def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1686        if frame.eval_ctx.volatile:
1687            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1688        elif frame.eval_ctx.autoescape:
1689            func_name = "markup_join"
1690        else:
1691            func_name = "str_join"
1692        self.write(f"{func_name}((")
1693        for arg in node.nodes:
1694            self.visit(arg, frame)
1695            self.write(", ")
1696        self.write("))")
@optimizeconst
def visit_Compare(self, node: jinja2.nodes.Compare, frame: Frame) -> None:
1698    @optimizeconst
1699    def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1700        self.write("(")
1701        self.visit(node.expr, frame)
1702        for op in node.ops:
1703            self.visit(op, frame)
1704        self.write(")")
def visit_Operand(self, node: jinja2.nodes.Operand, frame: Frame) -> None:
1706    def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1707        self.write(f" {operators[node.op]} ")
1708        self.visit(node.expr, frame)
@optimizeconst
def visit_Getattr(self, node: jinja2.nodes.Getattr, frame: Frame) -> None:
1710    @optimizeconst
1711    def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1712        if self.environment.is_async:
1713            self.write("(await auto_await(")
1714
1715        self.write("environment.getattr(")
1716        self.visit(node.node, frame)
1717        self.write(f", {node.attr!r})")
1718
1719        if self.environment.is_async:
1720            self.write("))")
@optimizeconst
def visit_Getitem(self, node: jinja2.nodes.Getitem, frame: Frame) -> None:
1722    @optimizeconst
1723    def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1724        # slices bypass the environment getitem method.
1725        if isinstance(node.arg, nodes.Slice):
1726            self.visit(node.node, frame)
1727            self.write("[")
1728            self.visit(node.arg, frame)
1729            self.write("]")
1730        else:
1731            if self.environment.is_async:
1732                self.write("(await auto_await(")
1733
1734            self.write("environment.getitem(")
1735            self.visit(node.node, frame)
1736            self.write(", ")
1737            self.visit(node.arg, frame)
1738            self.write(")")
1739
1740            if self.environment.is_async:
1741                self.write("))")
def visit_Slice(self, node: jinja2.nodes.Slice, frame: Frame) -> None:
1743    def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1744        if node.start is not None:
1745            self.visit(node.start, frame)
1746        self.write(":")
1747        if node.stop is not None:
1748            self.visit(node.stop, frame)
1749        if node.step is not None:
1750            self.write(":")
1751            self.visit(node.step, frame)
@optimizeconst
def visit_Filter(self, node: jinja2.nodes.Filter, frame: Frame) -> None:
1795    @optimizeconst
1796    def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1797        with self._filter_test_common(node, frame, True):
1798            # if the filter node is None we are inside a filter block
1799            # and want to write to the current buffer
1800            if node.node is not None:
1801                self.visit(node.node, frame)
1802            elif frame.eval_ctx.volatile:
1803                self.write(
1804                    f"(Markup(concat({frame.buffer}))"
1805                    f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1806                )
1807            elif frame.eval_ctx.autoescape:
1808                self.write(f"Markup(concat({frame.buffer}))")
1809            else:
1810                self.write(f"concat({frame.buffer})")
@optimizeconst
def visit_Test(self, node: jinja2.nodes.Test, frame: Frame) -> None:
1812    @optimizeconst
1813    def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1814        with self._filter_test_common(node, frame, False):
1815            self.visit(node.node, frame)
@optimizeconst
def visit_CondExpr(self, node: jinja2.nodes.CondExpr, frame: Frame) -> None:
1817    @optimizeconst
1818    def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1819        frame = frame.soft()
1820
1821        def write_expr2() -> None:
1822            if node.expr2 is not None:
1823                self.visit(node.expr2, frame)
1824                return
1825
1826            self.write(
1827                f'cond_expr_undefined("the inline if-expression on'
1828                f" {self.position(node)} evaluated to false and no else"
1829                f' section was defined.")'
1830            )
1831
1832        self.write("(")
1833        self.visit(node.expr1, frame)
1834        self.write(" if ")
1835        self.visit(node.test, frame)
1836        self.write(" else ")
1837        write_expr2()
1838        self.write(")")
@optimizeconst
def visit_Call( self, node: jinja2.nodes.Call, frame: Frame, forward_caller: bool = False) -> None:
1840    @optimizeconst
1841    def visit_Call(
1842        self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1843    ) -> None:
1844        if self.environment.is_async:
1845            self.write("(await auto_await(")
1846        if self.environment.sandboxed:
1847            self.write("environment.call(context, ")
1848        else:
1849            self.write("context.call(")
1850        self.visit(node.node, frame)
1851        extra_kwargs = {"caller": "caller"} if forward_caller else None
1852        loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1853        block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1854        if extra_kwargs:
1855            extra_kwargs.update(loop_kwargs, **block_kwargs)
1856        elif loop_kwargs or block_kwargs:
1857            extra_kwargs = dict(loop_kwargs, **block_kwargs)
1858        self.signature(node, frame, extra_kwargs)
1859        self.write(")")
1860        if self.environment.is_async:
1861            self.write("))")
def visit_Keyword(self, node: jinja2.nodes.Keyword, frame: Frame) -> None:
1863    def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1864        self.write(node.key + "=")
1865        self.visit(node.value, frame)
def visit_MarkSafe(self, node: jinja2.nodes.MarkSafe, frame: Frame) -> None:
1869    def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1870        self.write("Markup(")
1871        self.visit(node.expr, frame)
1872        self.write(")")
def visit_MarkSafeIfAutoescape( self, node: jinja2.nodes.MarkSafeIfAutoescape, frame: Frame) -> None:
1874    def visit_MarkSafeIfAutoescape(
1875        self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1876    ) -> None:
1877        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1878        self.visit(node.expr, frame)
1879        self.write(")")
def visit_EnvironmentAttribute( self, node: jinja2.nodes.EnvironmentAttribute, frame: Frame) -> None:
1881    def visit_EnvironmentAttribute(
1882        self, node: nodes.EnvironmentAttribute, frame: Frame
1883    ) -> None:
1884        self.write("environment." + node.name)
def visit_ExtensionAttribute( self, node: jinja2.nodes.ExtensionAttribute, frame: Frame) -> None:
1886    def visit_ExtensionAttribute(
1887        self, node: nodes.ExtensionAttribute, frame: Frame
1888    ) -> None:
1889        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
def visit_ImportedName( self, node: jinja2.nodes.ImportedName, frame: Frame) -> None:
1891    def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1892        self.write(self.import_aliases[node.importname])
def visit_InternalName( self, node: jinja2.nodes.InternalName, frame: Frame) -> None:
1894    def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1895        self.write(node.name)
def visit_ContextReference( self, node: jinja2.nodes.ContextReference, frame: Frame) -> None:
1897    def visit_ContextReference(
1898        self, node: nodes.ContextReference, frame: Frame
1899    ) -> None:
1900        self.write("context")
def visit_DerivedContextReference( self, node: jinja2.nodes.DerivedContextReference, frame: Frame) -> None:
1902    def visit_DerivedContextReference(
1903        self, node: nodes.DerivedContextReference, frame: Frame
1904    ) -> None:
1905        self.write(self.derive_context(frame))
def visit_Continue(self, node: jinja2.nodes.Continue, frame: Frame) -> None:
1907    def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1908        self.writeline("continue", node)
def visit_Break(self, node: jinja2.nodes.Break, frame: Frame) -> None:
1910    def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1911        self.writeline("break", node)
def visit_Scope(self, node: jinja2.nodes.Scope, frame: Frame) -> None:
1913    def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1914        scope_frame = frame.inner()
1915        scope_frame.symbols.analyze_node(node)
1916        self.enter_frame(scope_frame)
1917        self.blockvisit(node.body, scope_frame)
1918        self.leave_frame(scope_frame)
def visit_OverlayScope( self, node: jinja2.nodes.OverlayScope, frame: Frame) -> None:
1920    def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1921        ctx = self.temporary_identifier()
1922        self.writeline(f"{ctx} = {self.derive_context(frame)}")
1923        self.writeline(f"{ctx}.vars = ")
1924        self.visit(node.context, frame)
1925        self.push_context_reference(ctx)
1926
1927        scope_frame = frame.inner(isolated=True)
1928        scope_frame.symbols.analyze_node(node)
1929        self.enter_frame(scope_frame)
1930        self.blockvisit(node.body, scope_frame)
1931        self.leave_frame(scope_frame)
1932        self.pop_context_reference()
def visit_EvalContextModifier( self, node: jinja2.nodes.EvalContextModifier, frame: Frame) -> None:
1934    def visit_EvalContextModifier(
1935        self, node: nodes.EvalContextModifier, frame: Frame
1936    ) -> None:
1937        for keyword in node.options:
1938            self.writeline(f"context.eval_ctx.{keyword.key} = ")
1939            self.visit(keyword.value, frame)
1940            try:
1941                val = keyword.value.as_const(frame.eval_ctx)
1942            except nodes.Impossible:
1943                frame.eval_ctx.volatile = True
1944            else:
1945                setattr(frame.eval_ctx, keyword.key, val)
def visit_ScopedEvalContextModifier( self, node: jinja2.nodes.ScopedEvalContextModifier, frame: Frame) -> None:
1947    def visit_ScopedEvalContextModifier(
1948        self, node: nodes.ScopedEvalContextModifier, frame: Frame
1949    ) -> None:
1950        old_ctx_name = self.temporary_identifier()
1951        saved_ctx = frame.eval_ctx.save()
1952        self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1953        self.visit_EvalContextModifier(node, frame)
1954        for child in node.body:
1955            self.visit(child, frame)
1956        frame.eval_ctx.revert(saved_ctx)
1957        self.writeline(f"context.eval_ctx.revert({old_ctx_name})")