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