jinja2.compiler

Compiles nodes from the parser into Python code.

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

Dump the function def of a macro or call block.

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

Dump the macro definition for the def created by macro_body.

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

Return a human readable position for the node.

def dump_local_context(self, frame: Frame) -> str:
717    def dump_local_context(self, frame: Frame) -> str:
718        items_kv = ", ".join(
719            f"{name!r}: {target}"
720            for name, target in frame.symbols.dump_stores().items()
721        )
722        return f"{{{items_kv}}}"
def write_commons(self) -> None:
724    def write_commons(self) -> None:
725        """Writes a common preamble that is used by root and block functions.
726        Primarily this sets up common local helpers and enforces a generator
727        through a dead branch.
728        """
729        self.writeline("resolve = context.resolve_or_missing")
730        self.writeline("undefined = environment.undefined")
731        self.writeline("concat = environment.concat")
732        # always use the standard Undefined class for the implicit else of
733        # conditional expressions
734        self.writeline("cond_expr_undefined = Undefined")
735        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:
737    def push_parameter_definitions(self, frame: Frame) -> None:
738        """Pushes all parameter targets from the given frame into a local
739        stack that permits tracking of yet to be assigned parameters.  In
740        particular this enables the optimization from `visit_Name` to skip
741        undefined expressions for parameters in macros as macros can reference
742        otherwise unbound parameters.
743        """
744        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:
746    def pop_parameter_definitions(self) -> None:
747        """Pops the current parameter definitions set."""
748        self._param_def_block.pop()

Pops the current parameter definitions set.

def mark_parameter_stored(self, target: str) -> None:
750    def mark_parameter_stored(self, target: str) -> None:
751        """Marks a parameter in the current parameter definitions as stored.
752        This will skip the enforced undefined checks.
753        """
754        if self._param_def_block:
755            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:
757    def push_context_reference(self, target: str) -> None:
758        self._context_reference_stack.append(target)
def pop_context_reference(self) -> None:
760    def pop_context_reference(self) -> None:
761        self._context_reference_stack.pop()
def get_context_ref(self) -> str:
763    def get_context_ref(self) -> str:
764        return self._context_reference_stack[-1]
def get_resolve_func(self) -> str:
766    def get_resolve_func(self) -> str:
767        target = self._context_reference_stack[-1]
768        if target == "context":
769            return "resolve"
770        return f"{target}.resolve"
def derive_context(self, frame: Frame) -> str:
772    def derive_context(self, frame: Frame) -> str:
773        return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
def parameter_is_undeclared(self, target: str) -> bool:
775    def parameter_is_undeclared(self, target: str) -> bool:
776        """Checks if a given target is an undeclared parameter."""
777        if not self._param_def_block:
778            return False
779        return target in self._param_def_block[-1]

Checks if a given target is an undeclared parameter.

def push_assign_tracking(self) -> None:
781    def push_assign_tracking(self) -> None:
782        """Pushes a new layer for assignment tracking."""
783        self._assign_stack.append(set())

Pushes a new layer for assignment tracking.

def pop_assign_tracking(self, frame: Frame) -> None:
785    def pop_assign_tracking(self, frame: Frame) -> None:
786        """Pops the topmost level for assignment tracking and updates the
787        context variables if necessary.
788        """
789        vars = self._assign_stack.pop()
790        if (
791            not frame.block_frame
792            and not frame.loop_frame
793            and not frame.toplevel
794            or not vars
795        ):
796            return
797        public_names = [x for x in vars if x[:1] != "_"]
798        if len(vars) == 1:
799            name = next(iter(vars))
800            ref = frame.symbols.ref(name)
801            if frame.loop_frame:
802                self.writeline(f"_loop_vars[{name!r}] = {ref}")
803                return
804            if frame.block_frame:
805                self.writeline(f"_block_vars[{name!r}] = {ref}")
806                return
807            self.writeline(f"context.vars[{name!r}] = {ref}")
808        else:
809            if frame.loop_frame:
810                self.writeline("_loop_vars.update({")
811            elif frame.block_frame:
812                self.writeline("_block_vars.update({")
813            else:
814                self.writeline("context.vars.update({")
815            for idx, name in enumerate(sorted(vars)):
816                if idx:
817                    self.write(", ")
818                ref = frame.symbols.ref(name)
819                self.write(f"{name!r}: {ref}")
820            self.write("})")
821        if not frame.block_frame and not frame.loop_frame and public_names:
822            if len(public_names) == 1:
823                self.writeline(f"context.exported_vars.add({public_names[0]!r})")
824            else:
825                names_str = ", ".join(map(repr, sorted(public_names)))
826                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:
830    def visit_Template(
831        self, node: nodes.Template, frame: t.Optional[Frame] = None
832    ) -> None:
833        assert frame is None, "no root frame allowed"
834        eval_ctx = EvalContext(self.environment, self.name)
835
836        from .runtime import async_exported
837        from .runtime import exported
838
839        if self.environment.is_async:
840            exported_names = sorted(exported + async_exported)
841        else:
842            exported_names = sorted(exported)
843
844        self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
845
846        # if we want a deferred initialization we cannot move the
847        # environment into a local name
848        envenv = "" if self.defer_init else ", environment=environment"
849
850        # do we have an extends tag at all?  If not, we can save some
851        # overhead by just not processing any inheritance code.
852        have_extends = node.find(nodes.Extends) is not None
853
854        # find all blocks
855        for block in node.find_all(nodes.Block):
856            if block.name in self.blocks:
857                self.fail(f"block {block.name!r} defined twice", block.lineno)
858            self.blocks[block.name] = block
859
860        # find all imports and import them
861        for import_ in node.find_all(nodes.ImportedName):
862            if import_.importname not in self.import_aliases:
863                imp = import_.importname
864                self.import_aliases[imp] = alias = self.temporary_identifier()
865                if "." in imp:
866                    module, obj = imp.rsplit(".", 1)
867                    self.writeline(f"from {module} import {obj} as {alias}")
868                else:
869                    self.writeline(f"import {imp} as {alias}")
870
871        # add the load name
872        self.writeline(f"name = {self.name!r}")
873
874        # generate the root render function.
875        self.writeline(
876            f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
877        )
878        self.indent()
879        self.write_commons()
880
881        # process the root
882        frame = Frame(eval_ctx)
883        if "self" in find_undeclared(node.body, ("self",)):
884            ref = frame.symbols.declare_parameter("self")
885            self.writeline(f"{ref} = TemplateReference(context)")
886        frame.symbols.analyze_node(node)
887        frame.toplevel = frame.rootlevel = True
888        frame.require_output_check = have_extends and not self.has_known_extends
889        if have_extends:
890            self.writeline("parent_template = None")
891        self.enter_frame(frame)
892        self.pull_dependencies(node.body)
893        self.blockvisit(node.body, frame)
894        self.leave_frame(frame, with_python_scope=True)
895        self.outdent()
896
897        # make sure that the parent root is called.
898        if have_extends:
899            if not self.has_known_extends:
900                self.indent()
901                self.writeline("if parent_template is not None:")
902            self.indent()
903            if not self.environment.is_async:
904                self.writeline("yield from parent_template.root_render_func(context)")
905            else:
906                self.writeline("agen = parent_template.root_render_func(context)")
907                self.writeline("try:")
908                self.indent()
909                self.writeline("async for event in agen:")
910                self.indent()
911                self.writeline("yield event")
912                self.outdent()
913                self.outdent()
914                self.writeline("finally: await agen.aclose()")
915            self.outdent(1 + (not self.has_known_extends))
916
917        # at this point we now have the blocks collected and can visit them too.
918        for name, block in self.blocks.items():
919            self.writeline(
920                f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
921                block,
922                1,
923            )
924            self.indent()
925            self.write_commons()
926            # It's important that we do not make this frame a child of the
927            # toplevel template.  This would cause a variety of
928            # interesting issues with identifier tracking.
929            block_frame = Frame(eval_ctx)
930            block_frame.block_frame = True
931            undeclared = find_undeclared(block.body, ("self", "super"))
932            if "self" in undeclared:
933                ref = block_frame.symbols.declare_parameter("self")
934                self.writeline(f"{ref} = TemplateReference(context)")
935            if "super" in undeclared:
936                ref = block_frame.symbols.declare_parameter("super")
937                self.writeline(f"{ref} = context.super({name!r}, block_{name})")
938            block_frame.symbols.analyze_node(block)
939            block_frame.block = name
940            self.writeline("_block_vars = {}")
941            self.enter_frame(block_frame)
942            self.pull_dependencies(block.body)
943            self.blockvisit(block.body, block_frame)
944            self.leave_frame(block_frame, with_python_scope=True)
945            self.outdent()
946
947        blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
948        self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
949        debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
950        self.writeline(f"debug_info = {debug_kv_str!r}")
def visit_Block(self, node: jinja2.nodes.Block, frame: Frame) -> None:
952    def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
953        """Call a block and register it for the template."""
954        level = 0
955        if frame.toplevel:
956            # if we know that we are a child template, there is no need to
957            # check if we are one
958            if self.has_known_extends:
959                return
960            if self.extends_so_far > 0:
961                self.writeline("if parent_template is None:")
962                self.indent()
963                level += 1
964
965        if node.scoped:
966            context = self.derive_context(frame)
967        else:
968            context = self.get_context_ref()
969
970        if node.required:
971            self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
972            self.indent()
973            self.writeline(
974                f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
975                node,
976            )
977            self.outdent()
978
979        if not self.environment.is_async and frame.buffer is None:
980            self.writeline(
981                f"yield from context.blocks[{node.name!r}][0]({context})", node
982            )
983        else:
984            self.writeline(f"gen = context.blocks[{node.name!r}][0]({context})")
985            self.writeline("try:")
986            self.indent()
987            self.writeline(
988                f"{self.choose_async()}for event in gen:",
989                node,
990            )
991            self.indent()
992            self.simple_write("event", frame)
993            self.outdent()
994            self.outdent()
995            self.writeline(
996                f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}"
997            )
998
999        self.outdent(level)

Call a block and register it for the template.

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

Calls the extender.

def visit_Include(self, node: jinja2.nodes.Include, frame: Frame) -> None:
1043    def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
1044        """Handles includes."""
1045        if node.ignore_missing:
1046            self.writeline("try:")
1047            self.indent()
1048
1049        func_name = "get_or_select_template"
1050        if isinstance(node.template, nodes.Const):
1051            if isinstance(node.template.value, str):
1052                func_name = "get_template"
1053            elif isinstance(node.template.value, (tuple, list)):
1054                func_name = "select_template"
1055        elif isinstance(node.template, (nodes.Tuple, nodes.List)):
1056            func_name = "select_template"
1057
1058        self.writeline(f"template = environment.{func_name}(", node)
1059        self.visit(node.template, frame)
1060        self.write(f", {self.name!r})")
1061        if node.ignore_missing:
1062            self.outdent()
1063            self.writeline("except TemplateNotFound:")
1064            self.indent()
1065            self.writeline("pass")
1066            self.outdent()
1067            self.writeline("else:")
1068            self.indent()
1069
1070        def loop_body() -> None:
1071            self.indent()
1072            self.simple_write("event", frame)
1073            self.outdent()
1074
1075        if node.with_context:
1076            self.writeline(
1077                f"gen = template.root_render_func("
1078                "template.new_context(context.get_all(), True,"
1079                f" {self.dump_local_context(frame)}))"
1080            )
1081            self.writeline("try:")
1082            self.indent()
1083            self.writeline(f"{self.choose_async()}for event in gen:")
1084            loop_body()
1085            self.outdent()
1086            self.writeline(
1087                f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}"
1088            )
1089        elif self.environment.is_async:
1090            self.writeline(
1091                "for event in (await template._get_default_module_async())"
1092                "._body_stream:"
1093            )
1094            loop_body()
1095        else:
1096            self.writeline("yield from template._get_default_module()._body_stream")
1097
1098        if node.ignore_missing:
1099            self.outdent()

Handles includes.

def visit_Import(self, node: jinja2.nodes.Import, frame: Frame) -> None:
1116    def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1117        """Visit regular imports."""
1118        self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1119        if frame.toplevel:
1120            self.write(f"context.vars[{node.target!r}] = ")
1121
1122        self._import_common(node, frame)
1123
1124        if frame.toplevel and not node.target.startswith("_"):
1125            self.writeline(f"context.exported_vars.discard({node.target!r})")

Visit regular imports.

def visit_FromImport( self, node: jinja2.nodes.FromImport, frame: Frame) -> None:
1127    def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
1128        """Visit named imports."""
1129        self.newline(node)
1130        self.write("included_template = ")
1131        self._import_common(node, frame)
1132        var_names = []
1133        discarded_names = []
1134        for name in node.names:
1135            if isinstance(name, tuple):
1136                name, alias = name
1137            else:
1138                alias = name
1139            self.writeline(
1140                f"{frame.symbols.ref(alias)} ="
1141                f" getattr(included_template, {name!r}, missing)"
1142            )
1143            self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
1144            self.indent()
1145            # The position will contain the template name, and will be formatted
1146            # into a string that will be compiled into an f-string. Curly braces
1147            # in the name must be replaced with escapes so that they will not be
1148            # executed as part of the f-string.
1149            position = self.position(node).replace("{", "{{").replace("}", "}}")
1150            message = (
1151                "the template {included_template.__name__!r}"
1152                f" (imported on {position})"
1153                f" does not export the requested name {name!r}"
1154            )
1155            self.writeline(
1156                f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
1157            )
1158            self.outdent()
1159            if frame.toplevel:
1160                var_names.append(alias)
1161                if not alias.startswith("_"):
1162                    discarded_names.append(alias)
1163
1164        if var_names:
1165            if len(var_names) == 1:
1166                name = var_names[0]
1167                self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
1168            else:
1169                names_kv = ", ".join(
1170                    f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
1171                )
1172                self.writeline(f"context.vars.update({{{names_kv}}})")
1173        if discarded_names:
1174            if len(discarded_names) == 1:
1175                self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
1176            else:
1177                names_str = ", ".join(map(repr, discarded_names))
1178                self.writeline(
1179                    f"context.exported_vars.difference_update(({names_str}))"
1180                )

Visit named imports.

def visit_For(self, node: jinja2.nodes.For, frame: Frame) -> None:
1182    def visit_For(self, node: nodes.For, frame: Frame) -> None:
1183        loop_frame = frame.inner()
1184        loop_frame.loop_frame = True
1185        test_frame = frame.inner()
1186        else_frame = frame.inner()
1187
1188        # try to figure out if we have an extended loop.  An extended loop
1189        # is necessary if the loop is in recursive mode if the special loop
1190        # variable is accessed in the body if the body is a scoped block.
1191        extended_loop = (
1192            node.recursive
1193            or "loop"
1194            in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1195            or any(block.scoped for block in node.find_all(nodes.Block))
1196        )
1197
1198        loop_ref = None
1199        if extended_loop:
1200            loop_ref = loop_frame.symbols.declare_parameter("loop")
1201
1202        loop_frame.symbols.analyze_node(node, for_branch="body")
1203        if node.else_:
1204            else_frame.symbols.analyze_node(node, for_branch="else")
1205
1206        if node.test:
1207            loop_filter_func = self.temporary_identifier()
1208            test_frame.symbols.analyze_node(node, for_branch="test")
1209            self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
1210            self.indent()
1211            self.enter_frame(test_frame)
1212            self.writeline(self.choose_async("async for ", "for "))
1213            self.visit(node.target, loop_frame)
1214            self.write(" in ")
1215            self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
1216            self.write(":")
1217            self.indent()
1218            self.writeline("if ", node.test)
1219            self.visit(node.test, test_frame)
1220            self.write(":")
1221            self.indent()
1222            self.writeline("yield ")
1223            self.visit(node.target, loop_frame)
1224            self.outdent(3)
1225            self.leave_frame(test_frame, with_python_scope=True)
1226
1227        # if we don't have an recursive loop we have to find the shadowed
1228        # variables at that point.  Because loops can be nested but the loop
1229        # variable is a special one we have to enforce aliasing for it.
1230        if node.recursive:
1231            self.writeline(
1232                f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
1233            )
1234            self.indent()
1235            self.buffer(loop_frame)
1236
1237            # Use the same buffer for the else frame
1238            else_frame.buffer = loop_frame.buffer
1239
1240        # make sure the loop variable is a special one and raise a template
1241        # assertion error if a loop tries to write to loop
1242        if extended_loop:
1243            self.writeline(f"{loop_ref} = missing")
1244
1245        for name in node.find_all(nodes.Name):
1246            if name.ctx == "store" and name.name == "loop":
1247                self.fail(
1248                    "Can't assign to special loop variable in for-loop target",
1249                    name.lineno,
1250                )
1251
1252        if node.else_:
1253            iteration_indicator = self.temporary_identifier()
1254            self.writeline(f"{iteration_indicator} = 1")
1255
1256        self.writeline(self.choose_async("async for ", "for "), node)
1257        self.visit(node.target, loop_frame)
1258        if extended_loop:
1259            self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
1260        else:
1261            self.write(" in ")
1262
1263        if node.test:
1264            self.write(f"{loop_filter_func}(")
1265        if node.recursive:
1266            self.write("reciter")
1267        else:
1268            if self.environment.is_async and not extended_loop:
1269                self.write("auto_aiter(")
1270            self.visit(node.iter, frame)
1271            if self.environment.is_async and not extended_loop:
1272                self.write(")")
1273        if node.test:
1274            self.write(")")
1275
1276        if node.recursive:
1277            self.write(", undefined, loop_render_func, depth):")
1278        else:
1279            self.write(", undefined):" if extended_loop else ":")
1280
1281        self.indent()
1282        self.enter_frame(loop_frame)
1283
1284        self.writeline("_loop_vars = {}")
1285        self.blockvisit(node.body, loop_frame)
1286        if node.else_:
1287            self.writeline(f"{iteration_indicator} = 0")
1288        self.outdent()
1289        self.leave_frame(
1290            loop_frame, with_python_scope=node.recursive and not node.else_
1291        )
1292
1293        if node.else_:
1294            self.writeline(f"if {iteration_indicator}:")
1295            self.indent()
1296            self.enter_frame(else_frame)
1297            self.blockvisit(node.else_, else_frame)
1298            self.leave_frame(else_frame)
1299            self.outdent()
1300
1301        # if the node was recursive we have to return the buffer contents
1302        # and start the iteration code
1303        if node.recursive:
1304            self.return_buffer_contents(loop_frame)
1305            self.outdent()
1306            self.start_write(frame, node)
1307            self.write(f"{self.choose_async('await ')}loop(")
1308            if self.environment.is_async:
1309                self.write("auto_aiter(")
1310            self.visit(node.iter, frame)
1311            if self.environment.is_async:
1312                self.write(")")
1313            self.write(", loop)")
1314            self.end_write(frame)
1315
1316        # at the end of the iteration, clear any assignments made in the
1317        # loop from the top level
1318        if self._assign_stack:
1319            self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
def visit_If(self, node: jinja2.nodes.If, frame: Frame) -> None:
1321    def visit_If(self, node: nodes.If, frame: Frame) -> None:
1322        if_frame = frame.soft()
1323        self.writeline("if ", node)
1324        self.visit(node.test, if_frame)
1325        self.write(":")
1326        self.indent()
1327        self.blockvisit(node.body, if_frame)
1328        self.outdent()
1329        for elif_ in node.elif_:
1330            self.writeline("elif ", elif_)
1331            self.visit(elif_.test, if_frame)
1332            self.write(":")
1333            self.indent()
1334            self.blockvisit(elif_.body, if_frame)
1335            self.outdent()
1336        if node.else_:
1337            self.writeline("else:")
1338            self.indent()
1339            self.blockvisit(node.else_, if_frame)
1340            self.outdent()
def visit_Macro(self, node: jinja2.nodes.Macro, frame: Frame) -> None:
1342    def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
1343        macro_frame, macro_ref = self.macro_body(node, frame)
1344        self.newline()
1345        if frame.toplevel:
1346            if not node.name.startswith("_"):
1347                self.write(f"context.exported_vars.add({node.name!r})")
1348            self.writeline(f"context.vars[{node.name!r}] = ")
1349        self.write(f"{frame.symbols.ref(node.name)} = ")
1350        self.macro_def(macro_ref, macro_frame)
def visit_CallBlock(self, node: jinja2.nodes.CallBlock, frame: Frame) -> None:
1352    def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
1353        call_frame, macro_ref = self.macro_body(node, frame)
1354        self.writeline("caller = ")
1355        self.macro_def(macro_ref, call_frame)
1356        self.start_write(frame, node)
1357        self.visit_Call(node.call, frame, forward_caller=True)
1358        self.end_write(frame)
def visit_FilterBlock( self, node: jinja2.nodes.FilterBlock, frame: Frame) -> None:
1360    def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
1361        filter_frame = frame.inner()
1362        filter_frame.symbols.analyze_node(node)
1363        self.enter_frame(filter_frame)
1364        self.buffer(filter_frame)
1365        self.blockvisit(node.body, filter_frame)
1366        self.start_write(frame, node)
1367        self.visit_Filter(node.filter, filter_frame)
1368        self.end_write(frame)
1369        self.leave_frame(filter_frame)
def visit_With(self, node: jinja2.nodes.With, frame: Frame) -> None:
1371    def visit_With(self, node: nodes.With, frame: Frame) -> None:
1372        with_frame = frame.inner()
1373        with_frame.symbols.analyze_node(node)
1374        self.enter_frame(with_frame)
1375        for target, expr in zip(node.targets, node.values):
1376            self.newline()
1377            self.visit(target, with_frame)
1378            self.write(" = ")
1379            self.visit(expr, frame)
1380        self.blockvisit(node.body, with_frame)
1381        self.leave_frame(with_frame)
def visit_ExprStmt(self, node: jinja2.nodes.ExprStmt, frame: Frame) -> None:
1383    def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
1384        self.newline(node)
1385        self.visit(node.node, frame)
def visit_Output(self, node: jinja2.nodes.Output, frame: Frame) -> None:
1504    def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
1505        # If an extends is active, don't render outside a block.
1506        if frame.require_output_check:
1507            # A top-level extends is known to exist at compile time.
1508            if self.has_known_extends:
1509                return
1510
1511            self.writeline("if parent_template is None:")
1512            self.indent()
1513
1514        finalize = self._make_finalize()
1515        body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
1516
1517        # Evaluate constants at compile time if possible. Each item in
1518        # body will be either a list of static data or a node to be
1519        # evaluated at runtime.
1520        for child in node.nodes:
1521            try:
1522                if not (
1523                    # If the finalize function requires runtime context,
1524                    # constants can't be evaluated at compile time.
1525                    finalize.const
1526                    # Unless it's basic template data that won't be
1527                    # finalized anyway.
1528                    or isinstance(child, nodes.TemplateData)
1529                ):
1530                    raise nodes.Impossible()
1531
1532                const = self._output_child_to_const(child, frame, finalize)
1533            except (nodes.Impossible, Exception):
1534                # The node was not constant and needs to be evaluated at
1535                # runtime. Or another error was raised, which is easier
1536                # to debug at runtime.
1537                body.append(child)
1538                continue
1539
1540            if body and isinstance(body[-1], list):
1541                body[-1].append(const)
1542            else:
1543                body.append([const])
1544
1545        if frame.buffer is not None:
1546            if len(body) == 1:
1547                self.writeline(f"{frame.buffer}.append(")
1548            else:
1549                self.writeline(f"{frame.buffer}.extend((")
1550
1551            self.indent()
1552
1553        for item in body:
1554            if isinstance(item, list):
1555                # A group of constant data to join and output.
1556                val = self._output_const_repr(item)
1557
1558                if frame.buffer is None:
1559                    self.writeline("yield " + val)
1560                else:
1561                    self.writeline(val + ",")
1562            else:
1563                if frame.buffer is None:
1564                    self.writeline("yield ", item)
1565                else:
1566                    self.newline(item)
1567
1568                # A node to be evaluated at runtime.
1569                self._output_child_pre(item, frame, finalize)
1570                self.visit(item, frame)
1571                self._output_child_post(item, frame, finalize)
1572
1573                if frame.buffer is not None:
1574                    self.write(",")
1575
1576        if frame.buffer is not None:
1577            self.outdent()
1578            self.writeline(")" if len(body) == 1 else "))")
1579
1580        if frame.require_output_check:
1581            self.outdent()
def visit_Assign(self, node: jinja2.nodes.Assign, frame: Frame) -> None:
1583    def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
1584        self.push_assign_tracking()
1585
1586        # ``a.b`` is allowed for assignment, and is parsed as an NSRef. However,
1587        # it is only valid if it references a Namespace object. Emit a check for
1588        # that for each ref here, before assignment code is emitted. This can't
1589        # be done in visit_NSRef as the ref could be in the middle of a tuple.
1590        seen_refs: t.Set[str] = set()
1591
1592        for nsref in node.find_all(nodes.NSRef):
1593            if nsref.name in seen_refs:
1594                # Only emit the check for each reference once, in case the same
1595                # ref is used multiple times in a tuple, `ns.a, ns.b = c, d`.
1596                continue
1597
1598            seen_refs.add(nsref.name)
1599            ref = frame.symbols.ref(nsref.name)
1600            self.writeline(f"if not isinstance({ref}, Namespace):")
1601            self.indent()
1602            self.writeline(
1603                "raise TemplateRuntimeError"
1604                '("cannot assign attribute on non-namespace object")'
1605            )
1606            self.outdent()
1607
1608        self.newline(node)
1609        self.visit(node.target, frame)
1610        self.write(" = ")
1611        self.visit(node.node, frame)
1612        self.pop_assign_tracking(frame)
def visit_AssignBlock( self, node: jinja2.nodes.AssignBlock, frame: Frame) -> None:
1614    def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
1615        self.push_assign_tracking()
1616        block_frame = frame.inner()
1617        # This is a special case.  Since a set block always captures we
1618        # will disable output checks.  This way one can use set blocks
1619        # toplevel even in extended templates.
1620        block_frame.require_output_check = False
1621        block_frame.symbols.analyze_node(node)
1622        self.enter_frame(block_frame)
1623        self.buffer(block_frame)
1624        self.blockvisit(node.body, block_frame)
1625        self.newline(node)
1626        self.visit(node.target, frame)
1627        self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
1628        if node.filter is not None:
1629            self.visit_Filter(node.filter, block_frame)
1630        else:
1631            self.write(f"concat({block_frame.buffer})")
1632        self.write(")")
1633        self.pop_assign_tracking(frame)
1634        self.leave_frame(block_frame)
def visit_Name(self, node: jinja2.nodes.Name, frame: Frame) -> None:
1638    def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1639        if node.ctx == "store" and (
1640            frame.toplevel or frame.loop_frame or frame.block_frame
1641        ):
1642            if self._assign_stack:
1643                self._assign_stack[-1].add(node.name)
1644        ref = frame.symbols.ref(node.name)
1645
1646        # If we are looking up a variable we might have to deal with the
1647        # case where it's undefined.  We can skip that case if the load
1648        # instruction indicates a parameter which are always defined.
1649        if node.ctx == "load":
1650            load = frame.symbols.find_load(ref)
1651            if not (
1652                load is not None
1653                and load[0] == VAR_LOAD_PARAMETER
1654                and not self.parameter_is_undeclared(ref)
1655            ):
1656                self.write(
1657                    f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
1658                )
1659                return
1660
1661        self.write(ref)
def visit_NSRef(self, node: jinja2.nodes.NSRef, frame: Frame) -> None:
1663    def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
1664        # NSRef is a dotted assignment target a.b=c, but uses a[b]=c internally.
1665        # visit_Assign emits code to validate that each ref is to a Namespace
1666        # object only. That can't be emitted here as the ref could be in the
1667        # middle of a tuple assignment.
1668        ref = frame.symbols.ref(node.name)
1669        self.writeline(f"{ref}[{node.attr!r}]")
def visit_Const(self, node: jinja2.nodes.Const, frame: Frame) -> None:
1671    def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
1672        val = node.as_const(frame.eval_ctx)
1673        if isinstance(val, float):
1674            self.write(str(val))
1675        else:
1676            self.write(repr(val))
def visit_TemplateData( self, node: jinja2.nodes.TemplateData, frame: Frame) -> None:
1678    def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
1679        try:
1680            self.write(repr(node.as_const(frame.eval_ctx)))
1681        except nodes.Impossible:
1682            self.write(
1683                f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
1684            )
def visit_Tuple(self, node: jinja2.nodes.Tuple, frame: Frame) -> None:
1686    def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
1687        self.write("(")
1688        idx = -1
1689        for idx, item in enumerate(node.items):
1690            if idx:
1691                self.write(", ")
1692            self.visit(item, frame)
1693        self.write(",)" if idx == 0 else ")")
def visit_List(self, node: jinja2.nodes.List, frame: Frame) -> None:
1695    def visit_List(self, node: nodes.List, frame: Frame) -> None:
1696        self.write("[")
1697        for idx, item in enumerate(node.items):
1698            if idx:
1699                self.write(", ")
1700            self.visit(item, frame)
1701        self.write("]")
def visit_Dict(self, node: jinja2.nodes.Dict, frame: Frame) -> None:
1703    def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
1704        self.write("{")
1705        for idx, item in enumerate(node.items):
1706            if idx:
1707                self.write(", ")
1708            self.visit(item.key, frame)
1709            self.write(": ")
1710            self.visit(item.value, frame)
1711        self.write("}")
@optimizeconst
def visit_Add( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Sub( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Mul( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Div( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_FloorDiv( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Pow( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Mod( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_And( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Or( self: CodeGenerator, node: jinja2.nodes.BinExpr, frame: Frame) -> None:
63    @optimizeconst
64    def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
65        if (
66            self.environment.sandboxed and op in self.environment.intercepted_binops  # type: ignore
67        ):
68            self.write(f"environment.call_binop(context, {op!r}, ")
69            self.visit(node.left, frame)
70            self.write(", ")
71            self.visit(node.right, frame)
72        else:
73            self.write("(")
74            self.visit(node.left, frame)
75            self.write(f" {op} ")
76            self.visit(node.right, frame)
77
78        self.write(")")

The type of the None singleton.

@optimizeconst
def visit_Pos( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
86    @optimizeconst
87    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
88        if (
89            self.environment.sandboxed 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(")")

The type of the None singleton.

@optimizeconst
def visit_Neg( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
86    @optimizeconst
87    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
88        if (
89            self.environment.sandboxed 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(")")

The type of the None singleton.

@optimizeconst
def visit_Not( self: CodeGenerator, node: jinja2.nodes.UnaryExpr, frame: Frame) -> None:
86    @optimizeconst
87    def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
88        if (
89            self.environment.sandboxed 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(")")

The type of the None singleton.

@optimizeconst
def visit_Concat(self, node: jinja2.nodes.Concat, frame: Frame) -> None:
1726    @optimizeconst
1727    def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
1728        if frame.eval_ctx.volatile:
1729            func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
1730        elif frame.eval_ctx.autoescape:
1731            func_name = "markup_join"
1732        else:
1733            func_name = "str_join"
1734        self.write(f"{func_name}((")
1735        for arg in node.nodes:
1736            self.visit(arg, frame)
1737            self.write(", ")
1738        self.write("))")
@optimizeconst
def visit_Compare(self, node: jinja2.nodes.Compare, frame: Frame) -> None:
1740    @optimizeconst
1741    def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
1742        self.write("(")
1743        self.visit(node.expr, frame)
1744        for op in node.ops:
1745            self.visit(op, frame)
1746        self.write(")")
def visit_Operand(self, node: jinja2.nodes.Operand, frame: Frame) -> None:
1748    def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1749        self.write(f" {operators[node.op]} ")
1750        self.visit(node.expr, frame)
@optimizeconst
def visit_Getattr(self, node: jinja2.nodes.Getattr, frame: Frame) -> None:
1752    @optimizeconst
1753    def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
1754        if self.environment.is_async:
1755            self.write("(await auto_await(")
1756
1757        self.write("environment.getattr(")
1758        self.visit(node.node, frame)
1759        self.write(f", {node.attr!r})")
1760
1761        if self.environment.is_async:
1762            self.write("))")
@optimizeconst
def visit_Getitem(self, node: jinja2.nodes.Getitem, frame: Frame) -> None:
1764    @optimizeconst
1765    def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
1766        # slices bypass the environment getitem method.
1767        if isinstance(node.arg, nodes.Slice):
1768            self.visit(node.node, frame)
1769            self.write("[")
1770            self.visit(node.arg, frame)
1771            self.write("]")
1772        else:
1773            if self.environment.is_async:
1774                self.write("(await auto_await(")
1775
1776            self.write("environment.getitem(")
1777            self.visit(node.node, frame)
1778            self.write(", ")
1779            self.visit(node.arg, frame)
1780            self.write(")")
1781
1782            if self.environment.is_async:
1783                self.write("))")
def visit_Slice(self, node: jinja2.nodes.Slice, frame: Frame) -> None:
1785    def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
1786        if node.start is not None:
1787            self.visit(node.start, frame)
1788        self.write(":")
1789        if node.stop is not None:
1790            self.visit(node.stop, frame)
1791        if node.step is not None:
1792            self.write(":")
1793            self.visit(node.step, frame)
@optimizeconst
def visit_Filter(self, node: jinja2.nodes.Filter, frame: Frame) -> None:
1837    @optimizeconst
1838    def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1839        with self._filter_test_common(node, frame, True):
1840            # if the filter node is None we are inside a filter block
1841            # and want to write to the current buffer
1842            if node.node is not None:
1843                self.visit(node.node, frame)
1844            elif frame.eval_ctx.volatile:
1845                self.write(
1846                    f"(Markup(concat({frame.buffer}))"
1847                    f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
1848                )
1849            elif frame.eval_ctx.autoescape:
1850                self.write(f"Markup(concat({frame.buffer}))")
1851            else:
1852                self.write(f"concat({frame.buffer})")
@optimizeconst
def visit_Test(self, node: jinja2.nodes.Test, frame: Frame) -> None:
1854    @optimizeconst
1855    def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1856        with self._filter_test_common(node, frame, False):
1857            self.visit(node.node, frame)
@optimizeconst
def visit_CondExpr(self, node: jinja2.nodes.CondExpr, frame: Frame) -> None:
1859    @optimizeconst
1860    def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1861        frame = frame.soft()
1862
1863        def write_expr2() -> None:
1864            if node.expr2 is not None:
1865                self.visit(node.expr2, frame)
1866                return
1867
1868            self.write(
1869                f'cond_expr_undefined("the inline if-expression on'
1870                f" {self.position(node)} evaluated to false and no else"
1871                f' section was defined.")'
1872            )
1873
1874        self.write("(")
1875        self.visit(node.expr1, frame)
1876        self.write(" if ")
1877        self.visit(node.test, frame)
1878        self.write(" else ")
1879        write_expr2()
1880        self.write(")")
@optimizeconst
def visit_Call( self, node: jinja2.nodes.Call, frame: Frame, forward_caller: bool = False) -> None:
1882    @optimizeconst
1883    def visit_Call(
1884        self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1885    ) -> None:
1886        if self.environment.is_async:
1887            self.write("(await auto_await(")
1888        if self.environment.sandboxed:
1889            self.write("environment.call(context, ")
1890        else:
1891            self.write("context.call(")
1892        self.visit(node.node, frame)
1893        extra_kwargs = {"caller": "caller"} if forward_caller else None
1894        loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1895        block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1896        if extra_kwargs:
1897            extra_kwargs.update(loop_kwargs, **block_kwargs)
1898        elif loop_kwargs or block_kwargs:
1899            extra_kwargs = dict(loop_kwargs, **block_kwargs)
1900        self.signature(node, frame, extra_kwargs)
1901        self.write(")")
1902        if self.environment.is_async:
1903            self.write("))")
def visit_Keyword(self, node: jinja2.nodes.Keyword, frame: Frame) -> None:
1905    def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
1906        self.write(node.key + "=")
1907        self.visit(node.value, frame)
def visit_MarkSafe(self, node: jinja2.nodes.MarkSafe, frame: Frame) -> None:
1911    def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
1912        self.write("Markup(")
1913        self.visit(node.expr, frame)
1914        self.write(")")
def visit_MarkSafeIfAutoescape( self, node: jinja2.nodes.MarkSafeIfAutoescape, frame: Frame) -> None:
1916    def visit_MarkSafeIfAutoescape(
1917        self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1918    ) -> None:
1919        self.write("(Markup if context.eval_ctx.autoescape else identity)(")
1920        self.visit(node.expr, frame)
1921        self.write(")")
def visit_EnvironmentAttribute( self, node: jinja2.nodes.EnvironmentAttribute, frame: Frame) -> None:
1923    def visit_EnvironmentAttribute(
1924        self, node: nodes.EnvironmentAttribute, frame: Frame
1925    ) -> None:
1926        self.write("environment." + node.name)
def visit_ExtensionAttribute( self, node: jinja2.nodes.ExtensionAttribute, frame: Frame) -> None:
1928    def visit_ExtensionAttribute(
1929        self, node: nodes.ExtensionAttribute, frame: Frame
1930    ) -> None:
1931        self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
def visit_ImportedName( self, node: jinja2.nodes.ImportedName, frame: Frame) -> None:
1933    def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
1934        self.write(self.import_aliases[node.importname])
def visit_InternalName( self, node: jinja2.nodes.InternalName, frame: Frame) -> None:
1936    def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
1937        self.write(node.name)
def visit_ContextReference( self, node: jinja2.nodes.ContextReference, frame: Frame) -> None:
1939    def visit_ContextReference(
1940        self, node: nodes.ContextReference, frame: Frame
1941    ) -> None:
1942        self.write("context")
def visit_DerivedContextReference( self, node: jinja2.nodes.DerivedContextReference, frame: Frame) -> None:
1944    def visit_DerivedContextReference(
1945        self, node: nodes.DerivedContextReference, frame: Frame
1946    ) -> None:
1947        self.write(self.derive_context(frame))
def visit_Continue(self, node: jinja2.nodes.Continue, frame: Frame) -> None:
1949    def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
1950        self.writeline("continue", node)
def visit_Break(self, node: jinja2.nodes.Break, frame: Frame) -> None:
1952    def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
1953        self.writeline("break", node)
def visit_Scope(self, node: jinja2.nodes.Scope, frame: Frame) -> None:
1955    def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
1956        scope_frame = frame.inner()
1957        scope_frame.symbols.analyze_node(node)
1958        self.enter_frame(scope_frame)
1959        self.blockvisit(node.body, scope_frame)
1960        self.leave_frame(scope_frame)
def visit_OverlayScope( self, node: jinja2.nodes.OverlayScope, frame: Frame) -> None:
1962    def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
1963        ctx = self.temporary_identifier()
1964        self.writeline(f"{ctx} = {self.derive_context(frame)}")
1965        self.writeline(f"{ctx}.vars = ")
1966        self.visit(node.context, frame)
1967        self.push_context_reference(ctx)
1968
1969        scope_frame = frame.inner(isolated=True)
1970        scope_frame.symbols.analyze_node(node)
1971        self.enter_frame(scope_frame)
1972        self.blockvisit(node.body, scope_frame)
1973        self.leave_frame(scope_frame)
1974        self.pop_context_reference()
def visit_EvalContextModifier( self, node: jinja2.nodes.EvalContextModifier, frame: Frame) -> None:
1976    def visit_EvalContextModifier(
1977        self, node: nodes.EvalContextModifier, frame: Frame
1978    ) -> None:
1979        for keyword in node.options:
1980            self.writeline(f"context.eval_ctx.{keyword.key} = ")
1981            self.visit(keyword.value, frame)
1982            try:
1983                val = keyword.value.as_const(frame.eval_ctx)
1984            except nodes.Impossible:
1985                frame.eval_ctx.volatile = True
1986            else:
1987                setattr(frame.eval_ctx, keyword.key, val)
def visit_ScopedEvalContextModifier( self, node: jinja2.nodes.ScopedEvalContextModifier, frame: Frame) -> None:
1989    def visit_ScopedEvalContextModifier(
1990        self, node: nodes.ScopedEvalContextModifier, frame: Frame
1991    ) -> None:
1992        old_ctx_name = self.temporary_identifier()
1993        saved_ctx = frame.eval_ctx.save()
1994        self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
1995        self.visit_EvalContextModifier(node, frame)
1996        for child in node.body:
1997            self.visit(child, frame)
1998        frame.eval_ctx.revert(saved_ctx)
1999        self.writeline(f"context.eval_ctx.revert({old_ctx_name})")