blob: 3458095f54ede1322eb2ab9e34288da87db54ca1 [file] [log] [blame]
Yuki Shiino57cb49722021-10-06 10:14:391"""Compiles nodes from the parser into Python code."""
Yuki Shiino2aec0732022-12-28 03:54:562import typing as t
3from contextlib import contextmanager
Takuto Ikuta9f3b732a2018-03-14 14:18:564from functools import update_wrapper
Yuki Shiino2aec0732022-12-28 03:54:565from io import StringIO
Yuki Shiino57cb49722021-10-06 10:14:396from itertools import chain
7from keyword import iskeyword as is_python_keyword
[email protected]88a0c562013-05-09 11:33:478
Yuki Shiino57cb49722021-10-06 10:14:399from markupsafe import escape
10from markupsafe import Markup
11
12from . import nodes
Yuki Shiino57cb49722021-10-06 10:14:3913from .exceptions import TemplateAssertionError
14from .idtracking import Symbols
15from .idtracking import VAR_LOAD_ALIAS
16from .idtracking import VAR_LOAD_PARAMETER
17from .idtracking import VAR_LOAD_RESOLVE
18from .idtracking import VAR_LOAD_UNDEFINED
19from .nodes import EvalContext
20from .optimizer import Optimizer
Yuki Shiino2aec0732022-12-28 03:54:5621from .utils import _PassArg
Yuki Shiino57cb49722021-10-06 10:14:3922from .utils import concat
23from .visitor import NodeVisitor
[email protected]88a0c562013-05-09 11:33:4724
Yuki Shiino2aec0732022-12-28 03:54:5625if t.TYPE_CHECKING:
26 import typing_extensions as te
27 from .environment import Environment
28
29F = t.TypeVar("F", bound=t.Callable[..., t.Any])
30
[email protected]88a0c562013-05-09 11:33:4731operators = {
Yuki Shiino57cb49722021-10-06 10:14:3932 "eq": "==",
33 "ne": "!=",
34 "gt": ">",
35 "gteq": ">=",
36 "lt": "<",
37 "lteq": "<=",
38 "in": "in",
39 "notin": "not in",
[email protected]88a0c562013-05-09 11:33:4740}
41
[email protected]88a0c562013-05-09 11:33:4742
Yuki Shiino2aec0732022-12-28 03:54:5643def optimizeconst(f: F) -> F:
44 def new_func(
45 self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any
46 ) -> t.Any:
Takuto Ikuta9f3b732a2018-03-14 14:18:5647 # Only optimize if the frame is not volatile
Yuki Shiino2aec0732022-12-28 03:54:5648 if self.optimizer is not None and not frame.eval_ctx.volatile:
Takuto Ikuta9f3b732a2018-03-14 14:18:5649 new_node = self.optimizer.visit(node, frame.eval_ctx)
Yuki Shiino2aec0732022-12-28 03:54:5650
Takuto Ikuta9f3b732a2018-03-14 14:18:5651 if new_node != node:
52 return self.visit(new_node, frame)
Yuki Shiino2aec0732022-12-28 03:54:5653
Takuto Ikuta9f3b732a2018-03-14 14:18:5654 return f(self, node, frame, **kwargs)
Yuki Shiino57cb49722021-10-06 10:14:3955
Yuki Shiino2aec0732022-12-28 03:54:5656 return update_wrapper(t.cast(F, new_func), f)
57
58
59def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]:
60 @optimizeconst
61 def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None:
62 if (
63 self.environment.sandboxed
64 and op in self.environment.intercepted_binops # type: ignore
65 ):
66 self.write(f"environment.call_binop(context, {op!r}, ")
67 self.visit(node.left, frame)
68 self.write(", ")
69 self.visit(node.right, frame)
70 else:
71 self.write("(")
72 self.visit(node.left, frame)
73 self.write(f" {op} ")
74 self.visit(node.right, frame)
75
76 self.write(")")
77
78 return visitor
79
80
81def _make_unop(
82 op: str,
83) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]:
84 @optimizeconst
85 def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None:
86 if (
87 self.environment.sandboxed
88 and op in self.environment.intercepted_unops # type: ignore
89 ):
90 self.write(f"environment.call_unop(context, {op!r}, ")
91 self.visit(node.node, frame)
92 else:
93 self.write("(" + op)
94 self.visit(node.node, frame)
95
96 self.write(")")
97
98 return visitor
[email protected]88a0c562013-05-09 11:33:4799
100
Yuki Shiino57cb49722021-10-06 10:14:39101def generate(
Yuki Shiino2aec0732022-12-28 03:54:56102 node: nodes.Template,
103 environment: "Environment",
104 name: t.Optional[str],
105 filename: t.Optional[str],
106 stream: t.Optional[t.TextIO] = None,
107 defer_init: bool = False,
108 optimized: bool = True,
109) -> t.Optional[str]:
[email protected]88a0c562013-05-09 11:33:47110 """Generate the python source for a node tree."""
111 if not isinstance(node, nodes.Template):
Yuki Shiino57cb49722021-10-06 10:14:39112 raise TypeError("Can't compile non template nodes")
Yuki Shiino2aec0732022-12-28 03:54:56113
Yuki Shiino57cb49722021-10-06 10:14:39114 generator = environment.code_generator_class(
115 environment, name, filename, stream, defer_init, optimized
116 )
[email protected]88a0c562013-05-09 11:33:47117 generator.visit(node)
Yuki Shiino2aec0732022-12-28 03:54:56118
[email protected]88a0c562013-05-09 11:33:47119 if stream is None:
Yuki Shiino2aec0732022-12-28 03:54:56120 return generator.stream.getvalue() # type: ignore
121
122 return None
[email protected]88a0c562013-05-09 11:33:47123
124
Yuki Shiino2aec0732022-12-28 03:54:56125def has_safe_repr(value: t.Any) -> bool:
[email protected]88a0c562013-05-09 11:33:47126 """Does the node have a safe representation?"""
127 if value is None or value is NotImplemented or value is Ellipsis:
128 return True
Yuki Shiino2aec0732022-12-28 03:54:56129
130 if type(value) in {bool, int, float, complex, range, str, Markup}:
[email protected]88a0c562013-05-09 11:33:47131 return True
Yuki Shiino2aec0732022-12-28 03:54:56132
133 if type(value) in {tuple, list, set, frozenset}:
134 return all(has_safe_repr(v) for v in value)
135
136 if type(value) is dict:
137 return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items())
138
[email protected]88a0c562013-05-09 11:33:47139 return False
140
141
Yuki Shiino2aec0732022-12-28 03:54:56142def find_undeclared(
143 nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
144) -> t.Set[str]:
[email protected]88a0c562013-05-09 11:33:47145 """Check if the names passed are accessed undeclared. The return value
146 is a set of all the undeclared names from the sequence of names found.
147 """
148 visitor = UndeclaredNameVisitor(names)
149 try:
150 for node in nodes:
151 visitor.visit(node)
152 except VisitorExit:
153 pass
154 return visitor.undeclared
155
156
Yuki Shiino2aec0732022-12-28 03:54:56157class MacroRef:
158 def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56159 self.node = node
160 self.accesses_caller = False
161 self.accesses_kwargs = False
162 self.accesses_varargs = False
[email protected]88a0c562013-05-09 11:33:47163
164
Yuki Shiino2aec0732022-12-28 03:54:56165class Frame:
[email protected]88a0c562013-05-09 11:33:47166 """Holds compile time information for us."""
167
Yuki Shiino2aec0732022-12-28 03:54:56168 def __init__(
169 self,
170 eval_ctx: EvalContext,
171 parent: t.Optional["Frame"] = None,
172 level: t.Optional[int] = None,
173 ) -> None:
[email protected]88a0c562013-05-09 11:33:47174 self.eval_ctx = eval_ctx
Yuki Shiino2aec0732022-12-28 03:54:56175
176 # the parent of this frame
177 self.parent = parent
178
179 if parent is None:
180 self.symbols = Symbols(level=level)
181
182 # in some dynamic inheritance situations the compiler needs to add
183 # write tests around output statements.
184 self.require_output_check = False
185
186 # inside some tags we are using a buffer rather than yield statements.
187 # this for example affects {% filter %} or {% macro %}. If a frame
188 # is buffered this variable points to the name of the list used as
189 # buffer.
190 self.buffer: t.Optional[str] = None
191
192 # the name of the block we're in, otherwise None.
193 self.block: t.Optional[str] = None
194
195 else:
196 self.symbols = Symbols(parent.symbols, level=level)
197 self.require_output_check = parent.require_output_check
198 self.buffer = parent.buffer
199 self.block = parent.block
[email protected]88a0c562013-05-09 11:33:47200
201 # a toplevel frame is the root + soft frames such as if conditions.
202 self.toplevel = False
203
204 # the root frame is basically just the outermost frame, so no if
205 # conditions. This information is used to optimize inheritance
206 # situations.
207 self.rootlevel = False
208
Yuki Shiino2aec0732022-12-28 03:54:56209 # variables set inside of loops and blocks should not affect outer frames,
210 # but they still needs to be kept track of as part of the active context.
211 self.loop_frame = False
212 self.block_frame = False
[email protected]88a0c562013-05-09 11:33:47213
Yuki Shiino2aec0732022-12-28 03:54:56214 # track whether the frame is being used in an if-statement or conditional
215 # expression as it determines which errors should be raised during runtime
216 # or compile time.
217 self.soft_frame = False
[email protected]88a0c562013-05-09 11:33:47218
Yuki Shiino2aec0732022-12-28 03:54:56219 def copy(self) -> "Frame":
[email protected]88a0c562013-05-09 11:33:47220 """Create a copy of the current one."""
221 rv = object.__new__(self.__class__)
222 rv.__dict__.update(self.__dict__)
Takuto Ikuta9f3b732a2018-03-14 14:18:56223 rv.symbols = self.symbols.copy()
[email protected]88a0c562013-05-09 11:33:47224 return rv
225
Yuki Shiino2aec0732022-12-28 03:54:56226 def inner(self, isolated: bool = False) -> "Frame":
[email protected]88a0c562013-05-09 11:33:47227 """Return an inner frame."""
Takuto Ikuta9f3b732a2018-03-14 14:18:56228 if isolated:
229 return Frame(self.eval_ctx, level=self.symbols.level + 1)
[email protected]88a0c562013-05-09 11:33:47230 return Frame(self.eval_ctx, self)
231
Yuki Shiino2aec0732022-12-28 03:54:56232 def soft(self) -> "Frame":
[email protected]88a0c562013-05-09 11:33:47233 """Return a soft frame. A soft frame may not be modified as
234 standalone thing as it shares the resources with the frame it
235 was created of, but it's not a rootlevel frame any longer.
Takuto Ikuta9f3b732a2018-03-14 14:18:56236
Yuki Shiino2aec0732022-12-28 03:54:56237 This is only used to implement if-statements and conditional
238 expressions.
[email protected]88a0c562013-05-09 11:33:47239 """
240 rv = self.copy()
241 rv.rootlevel = False
Yuki Shiino2aec0732022-12-28 03:54:56242 rv.soft_frame = True
[email protected]88a0c562013-05-09 11:33:47243 return rv
244
245 __copy__ = copy
246
247
248class VisitorExit(RuntimeError):
249 """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
250
251
252class DependencyFinderVisitor(NodeVisitor):
253 """A visitor that collects filter and test calls."""
254
Yuki Shiino2aec0732022-12-28 03:54:56255 def __init__(self) -> None:
256 self.filters: t.Set[str] = set()
257 self.tests: t.Set[str] = set()
[email protected]88a0c562013-05-09 11:33:47258
Yuki Shiino2aec0732022-12-28 03:54:56259 def visit_Filter(self, node: nodes.Filter) -> None:
[email protected]88a0c562013-05-09 11:33:47260 self.generic_visit(node)
261 self.filters.add(node.name)
262
Yuki Shiino2aec0732022-12-28 03:54:56263 def visit_Test(self, node: nodes.Test) -> None:
[email protected]88a0c562013-05-09 11:33:47264 self.generic_visit(node)
265 self.tests.add(node.name)
266
Yuki Shiino2aec0732022-12-28 03:54:56267 def visit_Block(self, node: nodes.Block) -> None:
[email protected]88a0c562013-05-09 11:33:47268 """Stop visiting at blocks."""
269
270
271class UndeclaredNameVisitor(NodeVisitor):
272 """A visitor that checks if a name is accessed without being
273 declared. This is different from the frame visitor as it will
274 not stop at closure frames.
275 """
276
Yuki Shiino2aec0732022-12-28 03:54:56277 def __init__(self, names: t.Iterable[str]) -> None:
[email protected]88a0c562013-05-09 11:33:47278 self.names = set(names)
Yuki Shiino2aec0732022-12-28 03:54:56279 self.undeclared: t.Set[str] = set()
[email protected]88a0c562013-05-09 11:33:47280
Yuki Shiino2aec0732022-12-28 03:54:56281 def visit_Name(self, node: nodes.Name) -> None:
Yuki Shiino57cb49722021-10-06 10:14:39282 if node.ctx == "load" and node.name in self.names:
[email protected]88a0c562013-05-09 11:33:47283 self.undeclared.add(node.name)
284 if self.undeclared == self.names:
285 raise VisitorExit()
286 else:
287 self.names.discard(node.name)
288
Yuki Shiino2aec0732022-12-28 03:54:56289 def visit_Block(self, node: nodes.Block) -> None:
[email protected]88a0c562013-05-09 11:33:47290 """Stop visiting a blocks."""
291
292
[email protected]88a0c562013-05-09 11:33:47293class CompilerExit(Exception):
294 """Raised if the compiler encountered a situation where it just
295 doesn't make sense to further process the code. Any block that
296 raises such an exception is not further processed.
297 """
298
299
300class CodeGenerator(NodeVisitor):
Yuki Shiino57cb49722021-10-06 10:14:39301 def __init__(
Yuki Shiino2aec0732022-12-28 03:54:56302 self,
303 environment: "Environment",
304 name: t.Optional[str],
305 filename: t.Optional[str],
306 stream: t.Optional[t.TextIO] = None,
307 defer_init: bool = False,
308 optimized: bool = True,
309 ) -> None:
[email protected]88a0c562013-05-09 11:33:47310 if stream is None:
Yuki Shiino2aec0732022-12-28 03:54:56311 stream = StringIO()
[email protected]88a0c562013-05-09 11:33:47312 self.environment = environment
313 self.name = name
314 self.filename = filename
315 self.stream = stream
316 self.created_block_context = False
317 self.defer_init = defer_init
Yuki Shiino2aec0732022-12-28 03:54:56318 self.optimizer: t.Optional[Optimizer] = None
319
Takuto Ikuta9f3b732a2018-03-14 14:18:56320 if optimized:
321 self.optimizer = Optimizer(environment)
[email protected]88a0c562013-05-09 11:33:47322
323 # aliases for imports
Yuki Shiino2aec0732022-12-28 03:54:56324 self.import_aliases: t.Dict[str, str] = {}
[email protected]88a0c562013-05-09 11:33:47325
326 # a registry for all blocks. Because blocks are moved out
327 # into the global python scope they are registered here
Yuki Shiino2aec0732022-12-28 03:54:56328 self.blocks: t.Dict[str, nodes.Block] = {}
[email protected]88a0c562013-05-09 11:33:47329
330 # the number of extends statements so far
331 self.extends_so_far = 0
332
333 # some templates have a rootlevel extends. In this case we
334 # can safely assume that we're a child template and do some
335 # more optimizations.
336 self.has_known_extends = False
337
338 # the current line number
339 self.code_lineno = 1
340
341 # registry of all filters and tests (global, not block local)
Yuki Shiino2aec0732022-12-28 03:54:56342 self.tests: t.Dict[str, str] = {}
343 self.filters: t.Dict[str, str] = {}
[email protected]88a0c562013-05-09 11:33:47344
345 # the debug information
Yuki Shiino2aec0732022-12-28 03:54:56346 self.debug_info: t.List[t.Tuple[int, int]] = []
347 self._write_debug_info: t.Optional[int] = None
[email protected]88a0c562013-05-09 11:33:47348
349 # the number of new lines before the next write()
350 self._new_lines = 0
351
352 # the line number of the last written statement
353 self._last_line = 0
354
355 # true if nothing was written so far.
356 self._first_write = True
357
358 # used by the `temporary_identifier` method to get new
359 # unique, temporary identifier
360 self._last_identifier = 0
361
362 # the current indentation
363 self._indentation = 0
364
Takuto Ikuta9f3b732a2018-03-14 14:18:56365 # Tracks toplevel assignments
Yuki Shiino2aec0732022-12-28 03:54:56366 self._assign_stack: t.List[t.Set[str]] = []
Takuto Ikuta9f3b732a2018-03-14 14:18:56367
368 # Tracks parameter definition blocks
Yuki Shiino2aec0732022-12-28 03:54:56369 self._param_def_block: t.List[t.Set[str]] = []
Takuto Ikuta9f3b732a2018-03-14 14:18:56370
371 # Tracks the current context.
Yuki Shiino57cb49722021-10-06 10:14:39372 self._context_reference_stack = ["context"]
Takuto Ikuta9f3b732a2018-03-14 14:18:56373
Yuki Shiino2aec0732022-12-28 03:54:56374 @property
375 def optimized(self) -> bool:
376 return self.optimizer is not None
377
[email protected]88a0c562013-05-09 11:33:47378 # -- Various compilation helpers
379
Yuki Shiino2aec0732022-12-28 03:54:56380 def fail(self, msg: str, lineno: int) -> "te.NoReturn":
[email protected]88a0c562013-05-09 11:33:47381 """Fail with a :exc:`TemplateAssertionError`."""
382 raise TemplateAssertionError(msg, lineno, self.name, self.filename)
383
Yuki Shiino2aec0732022-12-28 03:54:56384 def temporary_identifier(self) -> str:
[email protected]88a0c562013-05-09 11:33:47385 """Get a new unique identifier."""
386 self._last_identifier += 1
Yuki Shiino2aec0732022-12-28 03:54:56387 return f"t_{self._last_identifier}"
[email protected]88a0c562013-05-09 11:33:47388
Yuki Shiino2aec0732022-12-28 03:54:56389 def buffer(self, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47390 """Enable buffering for the frame from that point onwards."""
391 frame.buffer = self.temporary_identifier()
Yuki Shiino2aec0732022-12-28 03:54:56392 self.writeline(f"{frame.buffer} = []")
[email protected]88a0c562013-05-09 11:33:47393
Yuki Shiino2aec0732022-12-28 03:54:56394 def return_buffer_contents(
395 self, frame: Frame, force_unescaped: bool = False
396 ) -> None:
[email protected]88a0c562013-05-09 11:33:47397 """Return the buffer contents of the frame."""
Takuto Ikuta9f3b732a2018-03-14 14:18:56398 if not force_unescaped:
399 if frame.eval_ctx.volatile:
Yuki Shiino57cb49722021-10-06 10:14:39400 self.writeline("if context.eval_ctx.autoescape:")
Takuto Ikuta9f3b732a2018-03-14 14:18:56401 self.indent()
Yuki Shiino2aec0732022-12-28 03:54:56402 self.writeline(f"return Markup(concat({frame.buffer}))")
Takuto Ikuta9f3b732a2018-03-14 14:18:56403 self.outdent()
Yuki Shiino57cb49722021-10-06 10:14:39404 self.writeline("else:")
Takuto Ikuta9f3b732a2018-03-14 14:18:56405 self.indent()
Yuki Shiino2aec0732022-12-28 03:54:56406 self.writeline(f"return concat({frame.buffer})")
Takuto Ikuta9f3b732a2018-03-14 14:18:56407 self.outdent()
408 return
409 elif frame.eval_ctx.autoescape:
Yuki Shiino2aec0732022-12-28 03:54:56410 self.writeline(f"return Markup(concat({frame.buffer}))")
Takuto Ikuta9f3b732a2018-03-14 14:18:56411 return
Yuki Shiino2aec0732022-12-28 03:54:56412 self.writeline(f"return concat({frame.buffer})")
[email protected]88a0c562013-05-09 11:33:47413
Yuki Shiino2aec0732022-12-28 03:54:56414 def indent(self) -> None:
[email protected]88a0c562013-05-09 11:33:47415 """Indent by one."""
416 self._indentation += 1
417
Yuki Shiino2aec0732022-12-28 03:54:56418 def outdent(self, step: int = 1) -> None:
[email protected]88a0c562013-05-09 11:33:47419 """Outdent by step."""
420 self._indentation -= step
421
Yuki Shiino2aec0732022-12-28 03:54:56422 def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None:
[email protected]88a0c562013-05-09 11:33:47423 """Yield or write into the frame buffer."""
424 if frame.buffer is None:
Yuki Shiino57cb49722021-10-06 10:14:39425 self.writeline("yield ", node)
[email protected]88a0c562013-05-09 11:33:47426 else:
Yuki Shiino2aec0732022-12-28 03:54:56427 self.writeline(f"{frame.buffer}.append(", node)
[email protected]88a0c562013-05-09 11:33:47428
Yuki Shiino2aec0732022-12-28 03:54:56429 def end_write(self, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47430 """End the writing process started by `start_write`."""
431 if frame.buffer is not None:
Yuki Shiino57cb49722021-10-06 10:14:39432 self.write(")")
[email protected]88a0c562013-05-09 11:33:47433
Yuki Shiino2aec0732022-12-28 03:54:56434 def simple_write(
435 self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None
436 ) -> None:
[email protected]88a0c562013-05-09 11:33:47437 """Simple shortcut for start_write + write + end_write."""
438 self.start_write(frame, node)
439 self.write(s)
440 self.end_write(frame)
441
Yuki Shiino2aec0732022-12-28 03:54:56442 def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47443 """Visit a list of nodes as block in a frame. If the current frame
Takuto Ikuta9f3b732a2018-03-14 14:18:56444 is no buffer a dummy ``if 0: yield None`` is written automatically.
[email protected]88a0c562013-05-09 11:33:47445 """
[email protected]88a0c562013-05-09 11:33:47446 try:
Yuki Shiino57cb49722021-10-06 10:14:39447 self.writeline("pass")
[email protected]88a0c562013-05-09 11:33:47448 for node in nodes:
449 self.visit(node, frame)
450 except CompilerExit:
451 pass
452
Yuki Shiino2aec0732022-12-28 03:54:56453 def write(self, x: str) -> None:
[email protected]88a0c562013-05-09 11:33:47454 """Write a string into the output stream."""
455 if self._new_lines:
456 if not self._first_write:
Yuki Shiino57cb49722021-10-06 10:14:39457 self.stream.write("\n" * self._new_lines)
[email protected]88a0c562013-05-09 11:33:47458 self.code_lineno += self._new_lines
459 if self._write_debug_info is not None:
Yuki Shiino57cb49722021-10-06 10:14:39460 self.debug_info.append((self._write_debug_info, self.code_lineno))
[email protected]88a0c562013-05-09 11:33:47461 self._write_debug_info = None
462 self._first_write = False
Yuki Shiino57cb49722021-10-06 10:14:39463 self.stream.write(" " * self._indentation)
[email protected]88a0c562013-05-09 11:33:47464 self._new_lines = 0
465 self.stream.write(x)
466
Yuki Shiino2aec0732022-12-28 03:54:56467 def writeline(
468 self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0
469 ) -> None:
[email protected]88a0c562013-05-09 11:33:47470 """Combination of newline and write."""
471 self.newline(node, extra)
472 self.write(x)
473
Yuki Shiino2aec0732022-12-28 03:54:56474 def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None:
[email protected]88a0c562013-05-09 11:33:47475 """Add one or more newlines before the next write."""
476 self._new_lines = max(self._new_lines, 1 + extra)
477 if node is not None and node.lineno != self._last_line:
478 self._write_debug_info = node.lineno
479 self._last_line = node.lineno
480
Yuki Shiino2aec0732022-12-28 03:54:56481 def signature(
482 self,
483 node: t.Union[nodes.Call, nodes.Filter, nodes.Test],
484 frame: Frame,
485 extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None,
486 ) -> None:
[email protected]88a0c562013-05-09 11:33:47487 """Writes a function call to the stream for the current node.
488 A leading comma is added automatically. The extra keyword
489 arguments may not include python keywords otherwise a syntax
Yuki Shiino57cb49722021-10-06 10:14:39490 error could occur. The extra keyword arguments should be given
[email protected]88a0c562013-05-09 11:33:47491 as python dict.
492 """
493 # if any of the given keyword arguments is a python keyword
494 # we have to make sure that no invalid call is created.
Yuki Shiino2aec0732022-12-28 03:54:56495 kwarg_workaround = any(
496 is_python_keyword(t.cast(str, k))
497 for k in chain((x.key for x in node.kwargs), extra_kwargs or ())
498 )
[email protected]88a0c562013-05-09 11:33:47499
500 for arg in node.args:
Yuki Shiino57cb49722021-10-06 10:14:39501 self.write(", ")
[email protected]88a0c562013-05-09 11:33:47502 self.visit(arg, frame)
503
504 if not kwarg_workaround:
505 for kwarg in node.kwargs:
Yuki Shiino57cb49722021-10-06 10:14:39506 self.write(", ")
[email protected]88a0c562013-05-09 11:33:47507 self.visit(kwarg, frame)
508 if extra_kwargs is not None:
Yuki Shiino2aec0732022-12-28 03:54:56509 for key, value in extra_kwargs.items():
510 self.write(f", {key}={value}")
[email protected]88a0c562013-05-09 11:33:47511 if node.dyn_args:
Yuki Shiino57cb49722021-10-06 10:14:39512 self.write(", *")
[email protected]88a0c562013-05-09 11:33:47513 self.visit(node.dyn_args, frame)
514
515 if kwarg_workaround:
516 if node.dyn_kwargs is not None:
Yuki Shiino57cb49722021-10-06 10:14:39517 self.write(", **dict({")
[email protected]88a0c562013-05-09 11:33:47518 else:
Yuki Shiino57cb49722021-10-06 10:14:39519 self.write(", **{")
[email protected]88a0c562013-05-09 11:33:47520 for kwarg in node.kwargs:
Yuki Shiino2aec0732022-12-28 03:54:56521 self.write(f"{kwarg.key!r}: ")
[email protected]88a0c562013-05-09 11:33:47522 self.visit(kwarg.value, frame)
Yuki Shiino57cb49722021-10-06 10:14:39523 self.write(", ")
[email protected]88a0c562013-05-09 11:33:47524 if extra_kwargs is not None:
Yuki Shiino2aec0732022-12-28 03:54:56525 for key, value in extra_kwargs.items():
526 self.write(f"{key!r}: {value}, ")
[email protected]88a0c562013-05-09 11:33:47527 if node.dyn_kwargs is not None:
Yuki Shiino57cb49722021-10-06 10:14:39528 self.write("}, **")
[email protected]88a0c562013-05-09 11:33:47529 self.visit(node.dyn_kwargs, frame)
Yuki Shiino57cb49722021-10-06 10:14:39530 self.write(")")
[email protected]88a0c562013-05-09 11:33:47531 else:
Yuki Shiino57cb49722021-10-06 10:14:39532 self.write("}")
[email protected]88a0c562013-05-09 11:33:47533
534 elif node.dyn_kwargs is not None:
Yuki Shiino57cb49722021-10-06 10:14:39535 self.write(", **")
[email protected]88a0c562013-05-09 11:33:47536 self.visit(node.dyn_kwargs, frame)
537
Yuki Shiino2aec0732022-12-28 03:54:56538 def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None:
539 """Find all filter and test names used in the template and
540 assign them to variables in the compiled namespace. Checking
541 that the names are registered with the environment is done when
542 compiling the Filter and Test nodes. If the node is in an If or
543 CondExpr node, the check is done at runtime instead.
544
545 .. versionchanged:: 3.0
546 Filters and tests in If and CondExpr nodes are checked at
547 runtime instead of compile time.
548 """
[email protected]88a0c562013-05-09 11:33:47549 visitor = DependencyFinderVisitor()
Yuki Shiino2aec0732022-12-28 03:54:56550
[email protected]88a0c562013-05-09 11:33:47551 for node in nodes:
552 visitor.visit(node)
[email protected]88a0c562013-05-09 11:33:47553
Yuki Shiino2aec0732022-12-28 03:54:56554 for id_map, names, dependency in (self.filters, visitor.filters, "filters"), (
555 self.tests,
556 visitor.tests,
557 "tests",
558 ):
559 for name in sorted(names):
560 if name not in id_map:
561 id_map[name] = self.temporary_identifier()
562
563 # add check during runtime that dependencies used inside of executed
564 # blocks are defined, as this step may be skipped during compile time
565 self.writeline("try:")
566 self.indent()
567 self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]")
568 self.outdent()
569 self.writeline("except KeyError:")
570 self.indent()
571 self.writeline("@internalcode")
572 self.writeline(f"def {id_map[name]}(*unused):")
573 self.indent()
574 self.writeline(
575 f'raise TemplateRuntimeError("No {dependency[:-1]}'
576 f' named {name!r} found.")'
577 )
578 self.outdent()
579 self.outdent()
580
581 def enter_frame(self, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56582 undefs = []
Yuki Shiino2aec0732022-12-28 03:54:56583 for target, (action, param) in frame.symbols.loads.items():
Takuto Ikuta9f3b732a2018-03-14 14:18:56584 if action == VAR_LOAD_PARAMETER:
585 pass
586 elif action == VAR_LOAD_RESOLVE:
Yuki Shiino2aec0732022-12-28 03:54:56587 self.writeline(f"{target} = {self.get_resolve_func()}({param!r})")
Takuto Ikuta9f3b732a2018-03-14 14:18:56588 elif action == VAR_LOAD_ALIAS:
Yuki Shiino2aec0732022-12-28 03:54:56589 self.writeline(f"{target} = {param}")
Takuto Ikuta9f3b732a2018-03-14 14:18:56590 elif action == VAR_LOAD_UNDEFINED:
591 undefs.append(target)
592 else:
Yuki Shiino57cb49722021-10-06 10:14:39593 raise NotImplementedError("unknown load instruction")
Takuto Ikuta9f3b732a2018-03-14 14:18:56594 if undefs:
Yuki Shiino2aec0732022-12-28 03:54:56595 self.writeline(f"{' = '.join(undefs)} = missing")
[email protected]88a0c562013-05-09 11:33:47596
Yuki Shiino2aec0732022-12-28 03:54:56597 def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56598 if not with_python_scope:
599 undefs = []
Yuki Shiino2aec0732022-12-28 03:54:56600 for target in frame.symbols.loads:
Takuto Ikuta9f3b732a2018-03-14 14:18:56601 undefs.append(target)
602 if undefs:
Yuki Shiino2aec0732022-12-28 03:54:56603 self.writeline(f"{' = '.join(undefs)} = missing")
[email protected]88a0c562013-05-09 11:33:47604
Yuki Shiino2aec0732022-12-28 03:54:56605 def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str:
606 return async_value if self.environment.is_async else sync_value
[email protected]88a0c562013-05-09 11:33:47607
Yuki Shiino2aec0732022-12-28 03:54:56608 def func(self, name: str) -> str:
609 return f"{self.choose_async()}def {name}"
610
611 def macro_body(
612 self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame
613 ) -> t.Tuple[Frame, MacroRef]:
Takuto Ikuta9f3b732a2018-03-14 14:18:56614 """Dump the function def of a macro or call block."""
615 frame = frame.inner()
616 frame.symbols.analyze_node(node)
617 macro_ref = MacroRef(node)
[email protected]88a0c562013-05-09 11:33:47618
Takuto Ikuta9f3b732a2018-03-14 14:18:56619 explicit_caller = None
620 skip_special_params = set()
621 args = []
Yuki Shiino2aec0732022-12-28 03:54:56622
Takuto Ikuta9f3b732a2018-03-14 14:18:56623 for idx, arg in enumerate(node.args):
Yuki Shiino57cb49722021-10-06 10:14:39624 if arg.name == "caller":
Takuto Ikuta9f3b732a2018-03-14 14:18:56625 explicit_caller = idx
Yuki Shiino57cb49722021-10-06 10:14:39626 if arg.name in ("kwargs", "varargs"):
Takuto Ikuta9f3b732a2018-03-14 14:18:56627 skip_special_params.add(arg.name)
628 args.append(frame.symbols.ref(arg.name))
[email protected]88a0c562013-05-09 11:33:47629
Yuki Shiino57cb49722021-10-06 10:14:39630 undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs"))
[email protected]88a0c562013-05-09 11:33:47631
Yuki Shiino57cb49722021-10-06 10:14:39632 if "caller" in undeclared:
633 # In older Jinja versions there was a bug that allowed caller
Takuto Ikuta9f3b732a2018-03-14 14:18:56634 # to retain the special behavior even if it was mentioned in
635 # the argument list. However thankfully this was only really
636 # working if it was the last argument. So we are explicitly
637 # checking this now and error out if it is anywhere else in
638 # the argument list.
639 if explicit_caller is not None:
640 try:
641 node.defaults[explicit_caller - len(node.args)]
642 except IndexError:
Yuki Shiino57cb49722021-10-06 10:14:39643 self.fail(
644 "When defining macros or call blocks the "
645 'special "caller" argument must be omitted '
646 "or be given a default.",
647 node.lineno,
648 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56649 else:
Yuki Shiino57cb49722021-10-06 10:14:39650 args.append(frame.symbols.declare_parameter("caller"))
Takuto Ikuta9f3b732a2018-03-14 14:18:56651 macro_ref.accesses_caller = True
Yuki Shiino57cb49722021-10-06 10:14:39652 if "kwargs" in undeclared and "kwargs" not in skip_special_params:
653 args.append(frame.symbols.declare_parameter("kwargs"))
Takuto Ikuta9f3b732a2018-03-14 14:18:56654 macro_ref.accesses_kwargs = True
Yuki Shiino57cb49722021-10-06 10:14:39655 if "varargs" in undeclared and "varargs" not in skip_special_params:
656 args.append(frame.symbols.declare_parameter("varargs"))
Takuto Ikuta9f3b732a2018-03-14 14:18:56657 macro_ref.accesses_varargs = True
[email protected]88a0c562013-05-09 11:33:47658
[email protected]88a0c562013-05-09 11:33:47659 # macros are delayed, they never require output checks
660 frame.require_output_check = False
Takuto Ikuta9f3b732a2018-03-14 14:18:56661 frame.symbols.analyze_node(node)
Yuki Shiino2aec0732022-12-28 03:54:56662 self.writeline(f"{self.func('macro')}({', '.join(args)}):", node)
[email protected]88a0c562013-05-09 11:33:47663 self.indent()
[email protected]88a0c562013-05-09 11:33:47664
Takuto Ikuta9f3b732a2018-03-14 14:18:56665 self.buffer(frame)
666 self.enter_frame(frame)
667
668 self.push_parameter_definitions(frame)
669 for idx, arg in enumerate(node.args):
670 ref = frame.symbols.ref(arg.name)
Yuki Shiino2aec0732022-12-28 03:54:56671 self.writeline(f"if {ref} is missing:")
Takuto Ikuta9f3b732a2018-03-14 14:18:56672 self.indent()
673 try:
674 default = node.defaults[idx - len(node.args)]
675 except IndexError:
Yuki Shiino57cb49722021-10-06 10:14:39676 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56677 f'{ref} = undefined("parameter {arg.name!r} was not provided",'
678 f" name={arg.name!r})"
Yuki Shiino57cb49722021-10-06 10:14:39679 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56680 else:
Yuki Shiino2aec0732022-12-28 03:54:56681 self.writeline(f"{ref} = ")
Takuto Ikuta9f3b732a2018-03-14 14:18:56682 self.visit(default, frame)
683 self.mark_parameter_stored(ref)
684 self.outdent()
685 self.pop_parameter_definitions()
686
687 self.blockvisit(node.body, frame)
688 self.return_buffer_contents(frame, force_unescaped=True)
689 self.leave_frame(frame, with_python_scope=True)
690 self.outdent()
691
692 return frame, macro_ref
693
Yuki Shiino2aec0732022-12-28 03:54:56694 def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47695 """Dump the macro definition for the def created by macro_body."""
Yuki Shiino57cb49722021-10-06 10:14:39696 arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args)
697 name = getattr(macro_ref.node, "name", None)
Takuto Ikuta9f3b732a2018-03-14 14:18:56698 if len(macro_ref.node.args) == 1:
Yuki Shiino57cb49722021-10-06 10:14:39699 arg_tuple += ","
700 self.write(
Yuki Shiino2aec0732022-12-28 03:54:56701 f"Macro(environment, macro, {name!r}, ({arg_tuple}),"
702 f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r},"
703 f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)"
Yuki Shiino57cb49722021-10-06 10:14:39704 )
[email protected]88a0c562013-05-09 11:33:47705
Yuki Shiino2aec0732022-12-28 03:54:56706 def position(self, node: nodes.Node) -> str:
[email protected]88a0c562013-05-09 11:33:47707 """Return a human readable position for the node."""
Yuki Shiino2aec0732022-12-28 03:54:56708 rv = f"line {node.lineno}"
[email protected]88a0c562013-05-09 11:33:47709 if self.name is not None:
Yuki Shiino2aec0732022-12-28 03:54:56710 rv = f"{rv} in {self.name!r}"
[email protected]88a0c562013-05-09 11:33:47711 return rv
712
Yuki Shiino2aec0732022-12-28 03:54:56713 def dump_local_context(self, frame: Frame) -> str:
714 items_kv = ", ".join(
715 f"{name!r}: {target}"
716 for name, target in frame.symbols.dump_stores().items()
Yuki Shiino57cb49722021-10-06 10:14:39717 )
Yuki Shiino2aec0732022-12-28 03:54:56718 return f"{{{items_kv}}}"
Takuto Ikuta9f3b732a2018-03-14 14:18:56719
Yuki Shiino2aec0732022-12-28 03:54:56720 def write_commons(self) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56721 """Writes a common preamble that is used by root and block functions.
722 Primarily this sets up common local helpers and enforces a generator
723 through a dead branch.
724 """
Yuki Shiino57cb49722021-10-06 10:14:39725 self.writeline("resolve = context.resolve_or_missing")
726 self.writeline("undefined = environment.undefined")
Yuki Shiino2aec0732022-12-28 03:54:56727 self.writeline("concat = environment.concat")
Yuki Shiino57cb49722021-10-06 10:14:39728 # always use the standard Undefined class for the implicit else of
729 # conditional expressions
730 self.writeline("cond_expr_undefined = Undefined")
731 self.writeline("if 0: yield None")
Takuto Ikuta9f3b732a2018-03-14 14:18:56732
Yuki Shiino2aec0732022-12-28 03:54:56733 def push_parameter_definitions(self, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56734 """Pushes all parameter targets from the given frame into a local
735 stack that permits tracking of yet to be assigned parameters. In
736 particular this enables the optimization from `visit_Name` to skip
737 undefined expressions for parameters in macros as macros can reference
738 otherwise unbound parameters.
739 """
740 self._param_def_block.append(frame.symbols.dump_param_targets())
741
Yuki Shiino2aec0732022-12-28 03:54:56742 def pop_parameter_definitions(self) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56743 """Pops the current parameter definitions set."""
744 self._param_def_block.pop()
745
Yuki Shiino2aec0732022-12-28 03:54:56746 def mark_parameter_stored(self, target: str) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56747 """Marks a parameter in the current parameter definitions as stored.
748 This will skip the enforced undefined checks.
749 """
750 if self._param_def_block:
751 self._param_def_block[-1].discard(target)
752
Yuki Shiino2aec0732022-12-28 03:54:56753 def push_context_reference(self, target: str) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56754 self._context_reference_stack.append(target)
755
Yuki Shiino2aec0732022-12-28 03:54:56756 def pop_context_reference(self) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56757 self._context_reference_stack.pop()
758
Yuki Shiino2aec0732022-12-28 03:54:56759 def get_context_ref(self) -> str:
Takuto Ikuta9f3b732a2018-03-14 14:18:56760 return self._context_reference_stack[-1]
761
Yuki Shiino2aec0732022-12-28 03:54:56762 def get_resolve_func(self) -> str:
Takuto Ikuta9f3b732a2018-03-14 14:18:56763 target = self._context_reference_stack[-1]
Yuki Shiino57cb49722021-10-06 10:14:39764 if target == "context":
765 return "resolve"
Yuki Shiino2aec0732022-12-28 03:54:56766 return f"{target}.resolve"
Takuto Ikuta9f3b732a2018-03-14 14:18:56767
Yuki Shiino2aec0732022-12-28 03:54:56768 def derive_context(self, frame: Frame) -> str:
769 return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})"
Takuto Ikuta9f3b732a2018-03-14 14:18:56770
Yuki Shiino2aec0732022-12-28 03:54:56771 def parameter_is_undeclared(self, target: str) -> bool:
Takuto Ikuta9f3b732a2018-03-14 14:18:56772 """Checks if a given target is an undeclared parameter."""
773 if not self._param_def_block:
774 return False
775 return target in self._param_def_block[-1]
776
Yuki Shiino2aec0732022-12-28 03:54:56777 def push_assign_tracking(self) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56778 """Pushes a new layer for assignment tracking."""
779 self._assign_stack.append(set())
780
Yuki Shiino2aec0732022-12-28 03:54:56781 def pop_assign_tracking(self, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:56782 """Pops the topmost level for assignment tracking and updates the
783 context variables if necessary.
784 """
785 vars = self._assign_stack.pop()
Yuki Shiino2aec0732022-12-28 03:54:56786 if (
787 not frame.block_frame
788 and not frame.loop_frame
789 and not frame.toplevel
790 or not vars
791 ):
Takuto Ikuta9f3b732a2018-03-14 14:18:56792 return
Yuki Shiino57cb49722021-10-06 10:14:39793 public_names = [x for x in vars if x[:1] != "_"]
Takuto Ikuta9f3b732a2018-03-14 14:18:56794 if len(vars) == 1:
795 name = next(iter(vars))
796 ref = frame.symbols.ref(name)
Yuki Shiino2aec0732022-12-28 03:54:56797 if frame.loop_frame:
798 self.writeline(f"_loop_vars[{name!r}] = {ref}")
799 return
800 if frame.block_frame:
801 self.writeline(f"_block_vars[{name!r}] = {ref}")
802 return
803 self.writeline(f"context.vars[{name!r}] = {ref}")
Takuto Ikuta9f3b732a2018-03-14 14:18:56804 else:
Yuki Shiino2aec0732022-12-28 03:54:56805 if frame.loop_frame:
806 self.writeline("_loop_vars.update({")
807 elif frame.block_frame:
808 self.writeline("_block_vars.update({")
809 else:
810 self.writeline("context.vars.update({")
Takuto Ikuta9f3b732a2018-03-14 14:18:56811 for idx, name in enumerate(vars):
812 if idx:
Yuki Shiino57cb49722021-10-06 10:14:39813 self.write(", ")
Takuto Ikuta9f3b732a2018-03-14 14:18:56814 ref = frame.symbols.ref(name)
Yuki Shiino2aec0732022-12-28 03:54:56815 self.write(f"{name!r}: {ref}")
Yuki Shiino57cb49722021-10-06 10:14:39816 self.write("})")
Yuki Shiino2aec0732022-12-28 03:54:56817 if not frame.block_frame and not frame.loop_frame and public_names:
Takuto Ikuta9f3b732a2018-03-14 14:18:56818 if len(public_names) == 1:
Yuki Shiino2aec0732022-12-28 03:54:56819 self.writeline(f"context.exported_vars.add({public_names[0]!r})")
Takuto Ikuta9f3b732a2018-03-14 14:18:56820 else:
Yuki Shiino2aec0732022-12-28 03:54:56821 names_str = ", ".join(map(repr, public_names))
822 self.writeline(f"context.exported_vars.update(({names_str}))")
Takuto Ikuta9f3b732a2018-03-14 14:18:56823
[email protected]88a0c562013-05-09 11:33:47824 # -- Statement Visitors
825
Yuki Shiino2aec0732022-12-28 03:54:56826 def visit_Template(
827 self, node: nodes.Template, frame: t.Optional[Frame] = None
828 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:39829 assert frame is None, "no root frame allowed"
[email protected]88a0c562013-05-09 11:33:47830 eval_ctx = EvalContext(self.environment, self.name)
831
Yuki Shiino2aec0732022-12-28 03:54:56832 from .runtime import exported, async_exported
Takuto Ikuta9f3b732a2018-03-14 14:18:56833
834 if self.environment.is_async:
Yuki Shiino2aec0732022-12-28 03:54:56835 exported_names = sorted(exported + async_exported)
836 else:
837 exported_names = sorted(exported)
838
839 self.writeline("from jinja2.runtime import " + ", ".join(exported_names))
[email protected]88a0c562013-05-09 11:33:47840
841 # if we want a deferred initialization we cannot move the
842 # environment into a local name
Yuki Shiino2aec0732022-12-28 03:54:56843 envenv = "" if self.defer_init else ", environment=environment"
[email protected]88a0c562013-05-09 11:33:47844
845 # do we have an extends tag at all? If not, we can save some
846 # overhead by just not processing any inheritance code.
847 have_extends = node.find(nodes.Extends) is not None
848
849 # find all blocks
850 for block in node.find_all(nodes.Block):
851 if block.name in self.blocks:
Yuki Shiino2aec0732022-12-28 03:54:56852 self.fail(f"block {block.name!r} defined twice", block.lineno)
[email protected]88a0c562013-05-09 11:33:47853 self.blocks[block.name] = block
854
855 # find all imports and import them
856 for import_ in node.find_all(nodes.ImportedName):
857 if import_.importname not in self.import_aliases:
858 imp = import_.importname
859 self.import_aliases[imp] = alias = self.temporary_identifier()
Yuki Shiino57cb49722021-10-06 10:14:39860 if "." in imp:
861 module, obj = imp.rsplit(".", 1)
Yuki Shiino2aec0732022-12-28 03:54:56862 self.writeline(f"from {module} import {obj} as {alias}")
[email protected]88a0c562013-05-09 11:33:47863 else:
Yuki Shiino2aec0732022-12-28 03:54:56864 self.writeline(f"import {imp} as {alias}")
[email protected]88a0c562013-05-09 11:33:47865
866 # add the load name
Yuki Shiino2aec0732022-12-28 03:54:56867 self.writeline(f"name = {self.name!r}")
[email protected]88a0c562013-05-09 11:33:47868
869 # generate the root render function.
Yuki Shiino57cb49722021-10-06 10:14:39870 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56871 f"{self.func('root')}(context, missing=missing{envenv}):", extra=1
Yuki Shiino57cb49722021-10-06 10:14:39872 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56873 self.indent()
874 self.write_commons()
[email protected]88a0c562013-05-09 11:33:47875
876 # process the root
877 frame = Frame(eval_ctx)
Yuki Shiino57cb49722021-10-06 10:14:39878 if "self" in find_undeclared(node.body, ("self",)):
879 ref = frame.symbols.declare_parameter("self")
Yuki Shiino2aec0732022-12-28 03:54:56880 self.writeline(f"{ref} = TemplateReference(context)")
Takuto Ikuta9f3b732a2018-03-14 14:18:56881 frame.symbols.analyze_node(node)
[email protected]88a0c562013-05-09 11:33:47882 frame.toplevel = frame.rootlevel = True
883 frame.require_output_check = have_extends and not self.has_known_extends
[email protected]88a0c562013-05-09 11:33:47884 if have_extends:
Yuki Shiino57cb49722021-10-06 10:14:39885 self.writeline("parent_template = None")
Takuto Ikuta9f3b732a2018-03-14 14:18:56886 self.enter_frame(frame)
[email protected]88a0c562013-05-09 11:33:47887 self.pull_dependencies(node.body)
888 self.blockvisit(node.body, frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:56889 self.leave_frame(frame, with_python_scope=True)
[email protected]88a0c562013-05-09 11:33:47890 self.outdent()
891
892 # make sure that the parent root is called.
893 if have_extends:
894 if not self.has_known_extends:
895 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:39896 self.writeline("if parent_template is not None:")
[email protected]88a0c562013-05-09 11:33:47897 self.indent()
Yuki Shiino2aec0732022-12-28 03:54:56898 if not self.environment.is_async:
Yuki Shiino57cb49722021-10-06 10:14:39899 self.writeline("yield from parent_template.root_render_func(context)")
Takuto Ikuta9f3b732a2018-03-14 14:18:56900 else:
Yuki Shiino57cb49722021-10-06 10:14:39901 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56902 "async for event in parent_template.root_render_func(context):"
Yuki Shiino57cb49722021-10-06 10:14:39903 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56904 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:39905 self.writeline("yield event")
Takuto Ikuta9f3b732a2018-03-14 14:18:56906 self.outdent()
907 self.outdent(1 + (not self.has_known_extends))
[email protected]88a0c562013-05-09 11:33:47908
909 # at this point we now have the blocks collected and can visit them too.
Yuki Shiino2aec0732022-12-28 03:54:56910 for name, block in self.blocks.items():
Yuki Shiino57cb49722021-10-06 10:14:39911 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56912 f"{self.func('block_' + name)}(context, missing=missing{envenv}):",
Yuki Shiino57cb49722021-10-06 10:14:39913 block,
914 1,
915 )
[email protected]88a0c562013-05-09 11:33:47916 self.indent()
Takuto Ikuta9f3b732a2018-03-14 14:18:56917 self.write_commons()
918 # It's important that we do not make this frame a child of the
919 # toplevel template. This would cause a variety of
920 # interesting issues with identifier tracking.
921 block_frame = Frame(eval_ctx)
Yuki Shiino2aec0732022-12-28 03:54:56922 block_frame.block_frame = True
Yuki Shiino57cb49722021-10-06 10:14:39923 undeclared = find_undeclared(block.body, ("self", "super"))
924 if "self" in undeclared:
925 ref = block_frame.symbols.declare_parameter("self")
Yuki Shiino2aec0732022-12-28 03:54:56926 self.writeline(f"{ref} = TemplateReference(context)")
Yuki Shiino57cb49722021-10-06 10:14:39927 if "super" in undeclared:
928 ref = block_frame.symbols.declare_parameter("super")
Yuki Shiino2aec0732022-12-28 03:54:56929 self.writeline(f"{ref} = context.super({name!r}, block_{name})")
Takuto Ikuta9f3b732a2018-03-14 14:18:56930 block_frame.symbols.analyze_node(block)
931 block_frame.block = name
Yuki Shiino2aec0732022-12-28 03:54:56932 self.writeline("_block_vars = {}")
Takuto Ikuta9f3b732a2018-03-14 14:18:56933 self.enter_frame(block_frame)
[email protected]88a0c562013-05-09 11:33:47934 self.pull_dependencies(block.body)
935 self.blockvisit(block.body, block_frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:56936 self.leave_frame(block_frame, with_python_scope=True)
[email protected]88a0c562013-05-09 11:33:47937 self.outdent()
938
Yuki Shiino2aec0732022-12-28 03:54:56939 blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks)
940 self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1)
941 debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info)
942 self.writeline(f"debug_info = {debug_kv_str!r}")
[email protected]88a0c562013-05-09 11:33:47943
Yuki Shiino2aec0732022-12-28 03:54:56944 def visit_Block(self, node: nodes.Block, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47945 """Call a block and register it for the template."""
Takuto Ikuta9f3b732a2018-03-14 14:18:56946 level = 0
[email protected]88a0c562013-05-09 11:33:47947 if frame.toplevel:
948 # if we know that we are a child template, there is no need to
949 # check if we are one
950 if self.has_known_extends:
951 return
952 if self.extends_so_far > 0:
Yuki Shiino57cb49722021-10-06 10:14:39953 self.writeline("if parent_template is None:")
[email protected]88a0c562013-05-09 11:33:47954 self.indent()
955 level += 1
Takuto Ikuta9f3b732a2018-03-14 14:18:56956
957 if node.scoped:
958 context = self.derive_context(frame)
959 else:
960 context = self.get_context_ref()
961
Yuki Shiino2aec0732022-12-28 03:54:56962 if node.required:
963 self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node)
964 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:39965 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56966 f'raise TemplateRuntimeError("Required block {node.name!r} not found")',
967 node,
968 )
969 self.outdent()
970
971 if not self.environment.is_async and frame.buffer is None:
972 self.writeline(
973 f"yield from context.blocks[{node.name!r}][0]({context})", node
Yuki Shiino57cb49722021-10-06 10:14:39974 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56975 else:
Yuki Shiino57cb49722021-10-06 10:14:39976 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:56977 f"{self.choose_async()}for event in"
978 f" context.blocks[{node.name!r}][0]({context}):",
Yuki Shiino57cb49722021-10-06 10:14:39979 node,
980 )
Takuto Ikuta9f3b732a2018-03-14 14:18:56981 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:39982 self.simple_write("event", frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:56983 self.outdent()
984
[email protected]88a0c562013-05-09 11:33:47985 self.outdent(level)
986
Yuki Shiino2aec0732022-12-28 03:54:56987 def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:47988 """Calls the extender."""
989 if not frame.toplevel:
Yuki Shiino57cb49722021-10-06 10:14:39990 self.fail("cannot use extend from a non top-level scope", node.lineno)
[email protected]88a0c562013-05-09 11:33:47991
992 # if the number of extends statements in general is zero so
993 # far, we don't have to add a check if something extended
994 # the template before this one.
995 if self.extends_so_far > 0:
996
997 # if we have a known extends we just add a template runtime
998 # error into the generated code. We could catch that at compile
999 # time too, but i welcome it not to confuse users by throwing the
1000 # same error at different times just "because we can".
1001 if not self.has_known_extends:
Yuki Shiino57cb49722021-10-06 10:14:391002 self.writeline("if parent_template is not None:")
[email protected]88a0c562013-05-09 11:33:471003 self.indent()
Yuki Shiino2aec0732022-12-28 03:54:561004 self.writeline('raise TemplateRuntimeError("extended multiple times")')
[email protected]88a0c562013-05-09 11:33:471005
1006 # if we have a known extends already we don't need that code here
1007 # as we know that the template execution will end here.
1008 if self.has_known_extends:
1009 raise CompilerExit()
[email protected]e6e05702013-09-06 09:48:341010 else:
1011 self.outdent()
[email protected]88a0c562013-05-09 11:33:471012
Yuki Shiino57cb49722021-10-06 10:14:391013 self.writeline("parent_template = environment.get_template(", node)
[email protected]88a0c562013-05-09 11:33:471014 self.visit(node.template, frame)
Yuki Shiino2aec0732022-12-28 03:54:561015 self.write(f", {self.name!r})")
1016 self.writeline("for name, parent_block in parent_template.blocks.items():")
[email protected]88a0c562013-05-09 11:33:471017 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391018 self.writeline("context.blocks.setdefault(name, []).append(parent_block)")
[email protected]88a0c562013-05-09 11:33:471019 self.outdent()
1020
1021 # if this extends statement was in the root level we can take
1022 # advantage of that information and simplify the generated code
1023 # in the top level from this point onwards
1024 if frame.rootlevel:
1025 self.has_known_extends = True
1026
1027 # and now we have one more
1028 self.extends_so_far += 1
1029
Yuki Shiino2aec0732022-12-28 03:54:561030 def visit_Include(self, node: nodes.Include, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471031 """Handles includes."""
[email protected]88a0c562013-05-09 11:33:471032 if node.ignore_missing:
Yuki Shiino57cb49722021-10-06 10:14:391033 self.writeline("try:")
[email protected]88a0c562013-05-09 11:33:471034 self.indent()
1035
Yuki Shiino57cb49722021-10-06 10:14:391036 func_name = "get_or_select_template"
[email protected]88a0c562013-05-09 11:33:471037 if isinstance(node.template, nodes.Const):
Yuki Shiino2aec0732022-12-28 03:54:561038 if isinstance(node.template.value, str):
Yuki Shiino57cb49722021-10-06 10:14:391039 func_name = "get_template"
[email protected]88a0c562013-05-09 11:33:471040 elif isinstance(node.template.value, (tuple, list)):
Yuki Shiino57cb49722021-10-06 10:14:391041 func_name = "select_template"
[email protected]88a0c562013-05-09 11:33:471042 elif isinstance(node.template, (nodes.Tuple, nodes.List)):
Yuki Shiino57cb49722021-10-06 10:14:391043 func_name = "select_template"
[email protected]88a0c562013-05-09 11:33:471044
Yuki Shiino2aec0732022-12-28 03:54:561045 self.writeline(f"template = environment.{func_name}(", node)
[email protected]88a0c562013-05-09 11:33:471046 self.visit(node.template, frame)
Yuki Shiino2aec0732022-12-28 03:54:561047 self.write(f", {self.name!r})")
[email protected]88a0c562013-05-09 11:33:471048 if node.ignore_missing:
1049 self.outdent()
Yuki Shiino57cb49722021-10-06 10:14:391050 self.writeline("except TemplateNotFound:")
[email protected]88a0c562013-05-09 11:33:471051 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391052 self.writeline("pass")
[email protected]88a0c562013-05-09 11:33:471053 self.outdent()
Yuki Shiino57cb49722021-10-06 10:14:391054 self.writeline("else:")
[email protected]88a0c562013-05-09 11:33:471055 self.indent()
1056
Takuto Ikuta9f3b732a2018-03-14 14:18:561057 skip_event_yield = False
[email protected]88a0c562013-05-09 11:33:471058 if node.with_context:
Yuki Shiino57cb49722021-10-06 10:14:391059 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561060 f"{self.choose_async()}for event in template.root_render_func("
1061 "template.new_context(context.get_all(), True,"
1062 f" {self.dump_local_context(frame)})):"
Yuki Shiino57cb49722021-10-06 10:14:391063 )
Takuto Ikuta9f3b732a2018-03-14 14:18:561064 elif self.environment.is_async:
Yuki Shiino57cb49722021-10-06 10:14:391065 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561066 "for event in (await template._get_default_module_async())"
Yuki Shiino57cb49722021-10-06 10:14:391067 "._body_stream:"
1068 )
[email protected]88a0c562013-05-09 11:33:471069 else:
Yuki Shiino2aec0732022-12-28 03:54:561070 self.writeline("yield from template._get_default_module()._body_stream")
1071 skip_event_yield = True
[email protected]88a0c562013-05-09 11:33:471072
Takuto Ikuta9f3b732a2018-03-14 14:18:561073 if not skip_event_yield:
1074 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391075 self.simple_write("event", frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561076 self.outdent()
[email protected]88a0c562013-05-09 11:33:471077
1078 if node.ignore_missing:
1079 self.outdent()
1080
Yuki Shiino2aec0732022-12-28 03:54:561081 def _import_common(
1082 self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame
1083 ) -> None:
1084 self.write(f"{self.choose_async('await ')}environment.get_template(")
[email protected]88a0c562013-05-09 11:33:471085 self.visit(node.template, frame)
Yuki Shiino2aec0732022-12-28 03:54:561086 self.write(f", {self.name!r}).")
[email protected]88a0c562013-05-09 11:33:471087
Yuki Shiino2aec0732022-12-28 03:54:561088 if node.with_context:
1089 f_name = f"make_module{self.choose_async('_async')}"
1090 self.write(
1091 f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})"
1092 )
1093 else:
1094 self.write(f"_get_default_module{self.choose_async('_async')}(context)")
1095
1096 def visit_Import(self, node: nodes.Import, frame: Frame) -> None:
1097 """Visit regular imports."""
1098 self.writeline(f"{frame.symbols.ref(node.target)} = ", node)
1099 if frame.toplevel:
1100 self.write(f"context.vars[{node.target!r}] = ")
1101
1102 self._import_common(node, frame)
1103
1104 if frame.toplevel and not node.target.startswith("_"):
1105 self.writeline(f"context.exported_vars.discard({node.target!r})")
1106
1107 def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471108 """Visit named imports."""
1109 self.newline(node)
Yuki Shiino2aec0732022-12-28 03:54:561110 self.write("included_template = ")
1111 self._import_common(node, frame)
[email protected]88a0c562013-05-09 11:33:471112 var_names = []
1113 discarded_names = []
1114 for name in node.names:
1115 if isinstance(name, tuple):
1116 name, alias = name
1117 else:
1118 alias = name
Yuki Shiino57cb49722021-10-06 10:14:391119 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561120 f"{frame.symbols.ref(alias)} ="
1121 f" getattr(included_template, {name!r}, missing)"
Yuki Shiino57cb49722021-10-06 10:14:391122 )
Yuki Shiino2aec0732022-12-28 03:54:561123 self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
[email protected]88a0c562013-05-09 11:33:471124 self.indent()
Yuki Shiino2aec0732022-12-28 03:54:561125 message = (
1126 "the template {included_template.__name__!r}"
1127 f" (imported on {self.position(node)})"
1128 f" does not export the requested name {name!r}"
1129 )
Yuki Shiino57cb49722021-10-06 10:14:391130 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561131 f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})"
Yuki Shiino57cb49722021-10-06 10:14:391132 )
[email protected]88a0c562013-05-09 11:33:471133 self.outdent()
1134 if frame.toplevel:
1135 var_names.append(alias)
Yuki Shiino57cb49722021-10-06 10:14:391136 if not alias.startswith("_"):
[email protected]88a0c562013-05-09 11:33:471137 discarded_names.append(alias)
[email protected]88a0c562013-05-09 11:33:471138
1139 if var_names:
1140 if len(var_names) == 1:
1141 name = var_names[0]
Yuki Shiino2aec0732022-12-28 03:54:561142 self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}")
[email protected]88a0c562013-05-09 11:33:471143 else:
Yuki Shiino2aec0732022-12-28 03:54:561144 names_kv = ", ".join(
1145 f"{name!r}: {frame.symbols.ref(name)}" for name in var_names
Yuki Shiino57cb49722021-10-06 10:14:391146 )
Yuki Shiino2aec0732022-12-28 03:54:561147 self.writeline(f"context.vars.update({{{names_kv}}})")
[email protected]88a0c562013-05-09 11:33:471148 if discarded_names:
1149 if len(discarded_names) == 1:
Yuki Shiino2aec0732022-12-28 03:54:561150 self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})")
[email protected]88a0c562013-05-09 11:33:471151 else:
Yuki Shiino2aec0732022-12-28 03:54:561152 names_str = ", ".join(map(repr, discarded_names))
Yuki Shiino57cb49722021-10-06 10:14:391153 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561154 f"context.exported_vars.difference_update(({names_str}))"
Yuki Shiino57cb49722021-10-06 10:14:391155 )
[email protected]88a0c562013-05-09 11:33:471156
Yuki Shiino2aec0732022-12-28 03:54:561157 def visit_For(self, node: nodes.For, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561158 loop_frame = frame.inner()
Yuki Shiino2aec0732022-12-28 03:54:561159 loop_frame.loop_frame = True
Takuto Ikuta9f3b732a2018-03-14 14:18:561160 test_frame = frame.inner()
1161 else_frame = frame.inner()
[email protected]88a0c562013-05-09 11:33:471162
1163 # try to figure out if we have an extended loop. An extended loop
1164 # is necessary if the loop is in recursive mode if the special loop
Yuki Shiino2aec0732022-12-28 03:54:561165 # variable is accessed in the body if the body is a scoped block.
1166 extended_loop = (
1167 node.recursive
1168 or "loop"
1169 in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",))
1170 or any(block.scoped for block in node.find_all(nodes.Block))
Yuki Shiino57cb49722021-10-06 10:14:391171 )
[email protected]88a0c562013-05-09 11:33:471172
Takuto Ikuta9f3b732a2018-03-14 14:18:561173 loop_ref = None
1174 if extended_loop:
Yuki Shiino57cb49722021-10-06 10:14:391175 loop_ref = loop_frame.symbols.declare_parameter("loop")
Takuto Ikuta9f3b732a2018-03-14 14:18:561176
Yuki Shiino57cb49722021-10-06 10:14:391177 loop_frame.symbols.analyze_node(node, for_branch="body")
Takuto Ikuta9f3b732a2018-03-14 14:18:561178 if node.else_:
Yuki Shiino57cb49722021-10-06 10:14:391179 else_frame.symbols.analyze_node(node, for_branch="else")
Takuto Ikuta9f3b732a2018-03-14 14:18:561180
1181 if node.test:
1182 loop_filter_func = self.temporary_identifier()
Yuki Shiino57cb49722021-10-06 10:14:391183 test_frame.symbols.analyze_node(node, for_branch="test")
Yuki Shiino2aec0732022-12-28 03:54:561184 self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test)
Takuto Ikuta9f3b732a2018-03-14 14:18:561185 self.indent()
1186 self.enter_frame(test_frame)
Yuki Shiino2aec0732022-12-28 03:54:561187 self.writeline(self.choose_async("async for ", "for "))
Takuto Ikuta9f3b732a2018-03-14 14:18:561188 self.visit(node.target, loop_frame)
Yuki Shiino57cb49722021-10-06 10:14:391189 self.write(" in ")
Yuki Shiino2aec0732022-12-28 03:54:561190 self.write(self.choose_async("auto_aiter(fiter)", "fiter"))
Yuki Shiino57cb49722021-10-06 10:14:391191 self.write(":")
Takuto Ikuta9f3b732a2018-03-14 14:18:561192 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391193 self.writeline("if ", node.test)
Takuto Ikuta9f3b732a2018-03-14 14:18:561194 self.visit(node.test, test_frame)
Yuki Shiino57cb49722021-10-06 10:14:391195 self.write(":")
Takuto Ikuta9f3b732a2018-03-14 14:18:561196 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391197 self.writeline("yield ")
Takuto Ikuta9f3b732a2018-03-14 14:18:561198 self.visit(node.target, loop_frame)
1199 self.outdent(3)
1200 self.leave_frame(test_frame, with_python_scope=True)
1201
[email protected]88a0c562013-05-09 11:33:471202 # if we don't have an recursive loop we have to find the shadowed
1203 # variables at that point. Because loops can be nested but the loop
1204 # variable is a special one we have to enforce aliasing for it.
Takuto Ikuta9f3b732a2018-03-14 14:18:561205 if node.recursive:
Yuki Shiino57cb49722021-10-06 10:14:391206 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561207 f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node
Yuki Shiino57cb49722021-10-06 10:14:391208 )
[email protected]88a0c562013-05-09 11:33:471209 self.indent()
1210 self.buffer(loop_frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561211
1212 # Use the same buffer for the else frame
1213 else_frame.buffer = loop_frame.buffer
[email protected]88a0c562013-05-09 11:33:471214
1215 # make sure the loop variable is a special one and raise a template
1216 # assertion error if a loop tries to write to loop
1217 if extended_loop:
Yuki Shiino2aec0732022-12-28 03:54:561218 self.writeline(f"{loop_ref} = missing")
Takuto Ikuta9f3b732a2018-03-14 14:18:561219
[email protected]88a0c562013-05-09 11:33:471220 for name in node.find_all(nodes.Name):
Yuki Shiino57cb49722021-10-06 10:14:391221 if name.ctx == "store" and name.name == "loop":
1222 self.fail(
1223 "Can't assign to special loop variable in for-loop target",
1224 name.lineno,
1225 )
[email protected]88a0c562013-05-09 11:33:471226
[email protected]88a0c562013-05-09 11:33:471227 if node.else_:
1228 iteration_indicator = self.temporary_identifier()
Yuki Shiino2aec0732022-12-28 03:54:561229 self.writeline(f"{iteration_indicator} = 1")
[email protected]88a0c562013-05-09 11:33:471230
Yuki Shiino2aec0732022-12-28 03:54:561231 self.writeline(self.choose_async("async for ", "for "), node)
[email protected]88a0c562013-05-09 11:33:471232 self.visit(node.target, loop_frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561233 if extended_loop:
Yuki Shiino2aec0732022-12-28 03:54:561234 self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(")
Takuto Ikuta9f3b732a2018-03-14 14:18:561235 else:
Yuki Shiino57cb49722021-10-06 10:14:391236 self.write(" in ")
[email protected]88a0c562013-05-09 11:33:471237
Takuto Ikuta9f3b732a2018-03-14 14:18:561238 if node.test:
Yuki Shiino2aec0732022-12-28 03:54:561239 self.write(f"{loop_filter_func}(")
Takuto Ikuta9f3b732a2018-03-14 14:18:561240 if node.recursive:
Yuki Shiino57cb49722021-10-06 10:14:391241 self.write("reciter")
[email protected]88a0c562013-05-09 11:33:471242 else:
Takuto Ikuta9f3b732a2018-03-14 14:18:561243 if self.environment.is_async and not extended_loop:
Yuki Shiino57cb49722021-10-06 10:14:391244 self.write("auto_aiter(")
Takuto Ikuta9f3b732a2018-03-14 14:18:561245 self.visit(node.iter, frame)
1246 if self.environment.is_async and not extended_loop:
Yuki Shiino57cb49722021-10-06 10:14:391247 self.write(")")
Takuto Ikuta9f3b732a2018-03-14 14:18:561248 if node.test:
Yuki Shiino57cb49722021-10-06 10:14:391249 self.write(")")
[email protected]88a0c562013-05-09 11:33:471250
1251 if node.recursive:
Yuki Shiino57cb49722021-10-06 10:14:391252 self.write(", undefined, loop_render_func, depth):")
[email protected]88a0c562013-05-09 11:33:471253 else:
Yuki Shiino2aec0732022-12-28 03:54:561254 self.write(", undefined):" if extended_loop else ":")
[email protected]88a0c562013-05-09 11:33:471255
1256 self.indent()
Takuto Ikuta9f3b732a2018-03-14 14:18:561257 self.enter_frame(loop_frame)
1258
Yuki Shiino2aec0732022-12-28 03:54:561259 self.writeline("_loop_vars = {}")
[email protected]88a0c562013-05-09 11:33:471260 self.blockvisit(node.body, loop_frame)
1261 if node.else_:
Yuki Shiino2aec0732022-12-28 03:54:561262 self.writeline(f"{iteration_indicator} = 0")
[email protected]88a0c562013-05-09 11:33:471263 self.outdent()
Yuki Shiino57cb49722021-10-06 10:14:391264 self.leave_frame(
1265 loop_frame, with_python_scope=node.recursive and not node.else_
1266 )
[email protected]88a0c562013-05-09 11:33:471267
1268 if node.else_:
Yuki Shiino2aec0732022-12-28 03:54:561269 self.writeline(f"if {iteration_indicator}:")
[email protected]88a0c562013-05-09 11:33:471270 self.indent()
Takuto Ikuta9f3b732a2018-03-14 14:18:561271 self.enter_frame(else_frame)
1272 self.blockvisit(node.else_, else_frame)
1273 self.leave_frame(else_frame)
[email protected]88a0c562013-05-09 11:33:471274 self.outdent()
1275
[email protected]88a0c562013-05-09 11:33:471276 # if the node was recursive we have to return the buffer contents
1277 # and start the iteration code
1278 if node.recursive:
1279 self.return_buffer_contents(loop_frame)
1280 self.outdent()
1281 self.start_write(frame, node)
Yuki Shiino2aec0732022-12-28 03:54:561282 self.write(f"{self.choose_async('await ')}loop(")
Takuto Ikuta9f3b732a2018-03-14 14:18:561283 if self.environment.is_async:
Yuki Shiino57cb49722021-10-06 10:14:391284 self.write("auto_aiter(")
[email protected]88a0c562013-05-09 11:33:471285 self.visit(node.iter, frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561286 if self.environment.is_async:
Yuki Shiino57cb49722021-10-06 10:14:391287 self.write(")")
1288 self.write(", loop)")
[email protected]88a0c562013-05-09 11:33:471289 self.end_write(frame)
1290
Yuki Shiino2aec0732022-12-28 03:54:561291 # at the end of the iteration, clear any assignments made in the
1292 # loop from the top level
1293 if self._assign_stack:
1294 self._assign_stack[-1].difference_update(loop_frame.symbols.stores)
1295
1296 def visit_If(self, node: nodes.If, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471297 if_frame = frame.soft()
Yuki Shiino57cb49722021-10-06 10:14:391298 self.writeline("if ", node)
[email protected]88a0c562013-05-09 11:33:471299 self.visit(node.test, if_frame)
Yuki Shiino57cb49722021-10-06 10:14:391300 self.write(":")
[email protected]88a0c562013-05-09 11:33:471301 self.indent()
1302 self.blockvisit(node.body, if_frame)
1303 self.outdent()
Takuto Ikuta9f3b732a2018-03-14 14:18:561304 for elif_ in node.elif_:
Yuki Shiino57cb49722021-10-06 10:14:391305 self.writeline("elif ", elif_)
Takuto Ikuta9f3b732a2018-03-14 14:18:561306 self.visit(elif_.test, if_frame)
Yuki Shiino57cb49722021-10-06 10:14:391307 self.write(":")
Takuto Ikuta9f3b732a2018-03-14 14:18:561308 self.indent()
1309 self.blockvisit(elif_.body, if_frame)
1310 self.outdent()
[email protected]88a0c562013-05-09 11:33:471311 if node.else_:
Yuki Shiino57cb49722021-10-06 10:14:391312 self.writeline("else:")
[email protected]88a0c562013-05-09 11:33:471313 self.indent()
1314 self.blockvisit(node.else_, if_frame)
1315 self.outdent()
1316
Yuki Shiino2aec0732022-12-28 03:54:561317 def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561318 macro_frame, macro_ref = self.macro_body(node, frame)
[email protected]88a0c562013-05-09 11:33:471319 self.newline()
1320 if frame.toplevel:
Yuki Shiino57cb49722021-10-06 10:14:391321 if not node.name.startswith("_"):
Yuki Shiino2aec0732022-12-28 03:54:561322 self.write(f"context.exported_vars.add({node.name!r})")
1323 self.writeline(f"context.vars[{node.name!r}] = ")
1324 self.write(f"{frame.symbols.ref(node.name)} = ")
Takuto Ikuta9f3b732a2018-03-14 14:18:561325 self.macro_def(macro_ref, macro_frame)
[email protected]88a0c562013-05-09 11:33:471326
Yuki Shiino2aec0732022-12-28 03:54:561327 def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561328 call_frame, macro_ref = self.macro_body(node, frame)
Yuki Shiino57cb49722021-10-06 10:14:391329 self.writeline("caller = ")
Takuto Ikuta9f3b732a2018-03-14 14:18:561330 self.macro_def(macro_ref, call_frame)
[email protected]88a0c562013-05-09 11:33:471331 self.start_write(frame, node)
Takuto Ikuta9f3b732a2018-03-14 14:18:561332 self.visit_Call(node.call, frame, forward_caller=True)
[email protected]88a0c562013-05-09 11:33:471333 self.end_write(frame)
1334
Yuki Shiino2aec0732022-12-28 03:54:561335 def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471336 filter_frame = frame.inner()
Takuto Ikuta9f3b732a2018-03-14 14:18:561337 filter_frame.symbols.analyze_node(node)
1338 self.enter_frame(filter_frame)
[email protected]88a0c562013-05-09 11:33:471339 self.buffer(filter_frame)
1340 self.blockvisit(node.body, filter_frame)
1341 self.start_write(frame, node)
1342 self.visit_Filter(node.filter, filter_frame)
1343 self.end_write(frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561344 self.leave_frame(filter_frame)
1345
Yuki Shiino2aec0732022-12-28 03:54:561346 def visit_With(self, node: nodes.With, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561347 with_frame = frame.inner()
1348 with_frame.symbols.analyze_node(node)
1349 self.enter_frame(with_frame)
Yuki Shiino2aec0732022-12-28 03:54:561350 for target, expr in zip(node.targets, node.values):
Takuto Ikuta9f3b732a2018-03-14 14:18:561351 self.newline()
1352 self.visit(target, with_frame)
Yuki Shiino57cb49722021-10-06 10:14:391353 self.write(" = ")
Takuto Ikuta9f3b732a2018-03-14 14:18:561354 self.visit(expr, frame)
1355 self.blockvisit(node.body, with_frame)
1356 self.leave_frame(with_frame)
[email protected]88a0c562013-05-09 11:33:471357
Yuki Shiino2aec0732022-12-28 03:54:561358 def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471359 self.newline(node)
1360 self.visit(node.node, frame)
1361
Yuki Shiino2aec0732022-12-28 03:54:561362 class _FinalizeInfo(t.NamedTuple):
1363 const: t.Optional[t.Callable[..., str]]
1364 src: t.Optional[str]
[email protected]88a0c562013-05-09 11:33:471365
Yuki Shiino2aec0732022-12-28 03:54:561366 @staticmethod
1367 def _default_finalize(value: t.Any) -> t.Any:
1368 """The default finalize function if the environment isn't
1369 configured with one. Or, if the environment has one, this is
1370 called on that function's output for constants.
1371 """
1372 return str(value)
1373
1374 _finalize: t.Optional[_FinalizeInfo] = None
1375
1376 def _make_finalize(self) -> _FinalizeInfo:
Yuki Shiino57cb49722021-10-06 10:14:391377 """Build the finalize function to be used on constants and at
1378 runtime. Cached so it's only created once for all output nodes.
1379
1380 Returns a ``namedtuple`` with the following attributes:
1381
1382 ``const``
1383 A function to finalize constant data at compile time.
1384
1385 ``src``
1386 Source code to output around nodes to be evaluated at
1387 runtime.
1388 """
1389 if self._finalize is not None:
1390 return self._finalize
1391
Yuki Shiino2aec0732022-12-28 03:54:561392 finalize: t.Optional[t.Callable[..., t.Any]]
Yuki Shiino57cb49722021-10-06 10:14:391393 finalize = default = self._default_finalize
1394 src = None
1395
[email protected]88a0c562013-05-09 11:33:471396 if self.environment.finalize:
Yuki Shiino57cb49722021-10-06 10:14:391397 src = "environment.finalize("
1398 env_finalize = self.environment.finalize
Yuki Shiino2aec0732022-12-28 03:54:561399 pass_arg = {
1400 _PassArg.context: "context",
1401 _PassArg.eval_context: "context.eval_ctx",
1402 _PassArg.environment: "environment",
1403 }.get(
1404 _PassArg.from_obj(env_finalize) # type: ignore
1405 )
1406 finalize = None
Yuki Shiino57cb49722021-10-06 10:14:391407
Yuki Shiino2aec0732022-12-28 03:54:561408 if pass_arg is None:
Yuki Shiino57cb49722021-10-06 10:14:391409
Yuki Shiino2aec0732022-12-28 03:54:561410 def finalize(value: t.Any) -> t.Any:
1411 return default(env_finalize(value))
Yuki Shiino57cb49722021-10-06 10:14:391412
Yuki Shiino2aec0732022-12-28 03:54:561413 else:
1414 src = f"{src}{pass_arg}, "
1415
1416 if pass_arg == "environment":
1417
1418 def finalize(value: t.Any) -> t.Any:
1419 return default(env_finalize(self.environment, value))
Yuki Shiino57cb49722021-10-06 10:14:391420
1421 self._finalize = self._FinalizeInfo(finalize, src)
1422 return self._finalize
1423
Yuki Shiino2aec0732022-12-28 03:54:561424 def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
Yuki Shiino57cb49722021-10-06 10:14:391425 """Given a group of constant values converted from ``Output``
1426 child nodes, produce a string to write to the template module
1427 source.
1428 """
1429 return repr(concat(group))
1430
Yuki Shiino2aec0732022-12-28 03:54:561431 def _output_child_to_const(
1432 self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1433 ) -> str:
Yuki Shiino57cb49722021-10-06 10:14:391434 """Try to optimize a child of an ``Output`` node by trying to
1435 convert it to constant, finalized data at compile time.
1436
1437 If :exc:`Impossible` is raised, the node is not constant and
1438 will be evaluated at runtime. Any other exception will also be
1439 evaluated at runtime for easier debugging.
1440 """
1441 const = node.as_const(frame.eval_ctx)
1442
1443 if frame.eval_ctx.autoescape:
1444 const = escape(const)
1445
1446 # Template data doesn't go through finalize.
1447 if isinstance(node, nodes.TemplateData):
Yuki Shiino2aec0732022-12-28 03:54:561448 return str(const)
Yuki Shiino57cb49722021-10-06 10:14:391449
Yuki Shiino2aec0732022-12-28 03:54:561450 return finalize.const(const) # type: ignore
Yuki Shiino57cb49722021-10-06 10:14:391451
Yuki Shiino2aec0732022-12-28 03:54:561452 def _output_child_pre(
1453 self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1454 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391455 """Output extra source code before visiting a child of an
1456 ``Output`` node.
1457 """
1458 if frame.eval_ctx.volatile:
Yuki Shiino2aec0732022-12-28 03:54:561459 self.write("(escape if context.eval_ctx.autoescape else str)(")
Yuki Shiino57cb49722021-10-06 10:14:391460 elif frame.eval_ctx.autoescape:
1461 self.write("escape(")
[email protected]88a0c562013-05-09 11:33:471462 else:
Yuki Shiino2aec0732022-12-28 03:54:561463 self.write("str(")
[email protected]88a0c562013-05-09 11:33:471464
Yuki Shiino57cb49722021-10-06 10:14:391465 if finalize.src is not None:
1466 self.write(finalize.src)
1467
Yuki Shiino2aec0732022-12-28 03:54:561468 def _output_child_post(
1469 self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo
1470 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391471 """Output extra source code after visiting a child of an
1472 ``Output`` node.
1473 """
1474 self.write(")")
1475
1476 if finalize.src is not None:
1477 self.write(")")
1478
Yuki Shiino2aec0732022-12-28 03:54:561479 def visit_Output(self, node: nodes.Output, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391480 # If an extends is active, don't render outside a block.
[email protected]88a0c562013-05-09 11:33:471481 if frame.require_output_check:
Yuki Shiino57cb49722021-10-06 10:14:391482 # A top-level extends is known to exist at compile time.
1483 if self.has_known_extends:
1484 return
[email protected]88a0c562013-05-09 11:33:471485
Yuki Shiino57cb49722021-10-06 10:14:391486 self.writeline("if parent_template is None:")
1487 self.indent()
1488
1489 finalize = self._make_finalize()
Yuki Shiino2aec0732022-12-28 03:54:561490 body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = []
Yuki Shiino57cb49722021-10-06 10:14:391491
1492 # Evaluate constants at compile time if possible. Each item in
1493 # body will be either a list of static data or a node to be
1494 # evaluated at runtime.
[email protected]88a0c562013-05-09 11:33:471495 for child in node.nodes:
1496 try:
Yuki Shiino57cb49722021-10-06 10:14:391497 if not (
1498 # If the finalize function requires runtime context,
1499 # constants can't be evaluated at compile time.
1500 finalize.const
1501 # Unless it's basic template data that won't be
1502 # finalized anyway.
1503 or isinstance(child, nodes.TemplateData)
1504 ):
yukishiinoa34bd6862016-09-08 04:27:091505 raise nodes.Impossible()
Yuki Shiino57cb49722021-10-06 10:14:391506
1507 const = self._output_child_to_const(child, frame, finalize)
1508 except (nodes.Impossible, Exception):
1509 # The node was not constant and needs to be evaluated at
1510 # runtime. Or another error was raised, which is easier
1511 # to debug at runtime.
[email protected]88a0c562013-05-09 11:33:471512 body.append(child)
1513 continue
Yuki Shiino57cb49722021-10-06 10:14:391514
[email protected]88a0c562013-05-09 11:33:471515 if body and isinstance(body[-1], list):
1516 body[-1].append(const)
1517 else:
1518 body.append([const])
1519
Yuki Shiino57cb49722021-10-06 10:14:391520 if frame.buffer is not None:
1521 if len(body) == 1:
Yuki Shiino2aec0732022-12-28 03:54:561522 self.writeline(f"{frame.buffer}.append(")
Yuki Shiino57cb49722021-10-06 10:14:391523 else:
Yuki Shiino2aec0732022-12-28 03:54:561524 self.writeline(f"{frame.buffer}.extend((")
[email protected]88a0c562013-05-09 11:33:471525
[email protected]88a0c562013-05-09 11:33:471526 self.indent()
[email protected]88a0c562013-05-09 11:33:471527
Yuki Shiino57cb49722021-10-06 10:14:391528 for item in body:
1529 if isinstance(item, list):
1530 # A group of constant data to join and output.
1531 val = self._output_const_repr(item)
1532
1533 if frame.buffer is None:
1534 self.writeline("yield " + val)
1535 else:
1536 self.writeline(val + ",")
1537 else:
1538 if frame.buffer is None:
1539 self.writeline("yield ", item)
1540 else:
1541 self.newline(item)
1542
1543 # A node to be evaluated at runtime.
1544 self._output_child_pre(item, frame, finalize)
1545 self.visit(item, frame)
1546 self._output_child_post(item, frame, finalize)
1547
1548 if frame.buffer is not None:
1549 self.write(",")
1550
1551 if frame.buffer is not None:
1552 self.outdent()
1553 self.writeline(")" if len(body) == 1 else "))")
1554
1555 if frame.require_output_check:
[email protected]88a0c562013-05-09 11:33:471556 self.outdent()
1557
Yuki Shiino2aec0732022-12-28 03:54:561558 def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561559 self.push_assign_tracking()
yukishiinoa34bd6862016-09-08 04:27:091560 self.newline(node)
Takuto Ikuta9f3b732a2018-03-14 14:18:561561 self.visit(node.target, frame)
Yuki Shiino57cb49722021-10-06 10:14:391562 self.write(" = ")
[email protected]88a0c562013-05-09 11:33:471563 self.visit(node.node, frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561564 self.pop_assign_tracking(frame)
[email protected]88a0c562013-05-09 11:33:471565
Yuki Shiino2aec0732022-12-28 03:54:561566 def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561567 self.push_assign_tracking()
yukishiinoa34bd6862016-09-08 04:27:091568 block_frame = frame.inner()
Takuto Ikuta9f3b732a2018-03-14 14:18:561569 # This is a special case. Since a set block always captures we
1570 # will disable output checks. This way one can use set blocks
1571 # toplevel even in extended templates.
1572 block_frame.require_output_check = False
1573 block_frame.symbols.analyze_node(node)
1574 self.enter_frame(block_frame)
yukishiinoa34bd6862016-09-08 04:27:091575 self.buffer(block_frame)
1576 self.blockvisit(node.body, block_frame)
yukishiinoa34bd6862016-09-08 04:27:091577 self.newline(node)
Takuto Ikuta9f3b732a2018-03-14 14:18:561578 self.visit(node.target, frame)
Yuki Shiino57cb49722021-10-06 10:14:391579 self.write(" = (Markup if context.eval_ctx.autoescape else identity)(")
Takuto Ikuta9f3b732a2018-03-14 14:18:561580 if node.filter is not None:
1581 self.visit_Filter(node.filter, block_frame)
1582 else:
Yuki Shiino2aec0732022-12-28 03:54:561583 self.write(f"concat({block_frame.buffer})")
Yuki Shiino57cb49722021-10-06 10:14:391584 self.write(")")
Takuto Ikuta9f3b732a2018-03-14 14:18:561585 self.pop_assign_tracking(frame)
1586 self.leave_frame(block_frame)
[email protected]88a0c562013-05-09 11:33:471587
1588 # -- Expression Visitors
1589
Yuki Shiino2aec0732022-12-28 03:54:561590 def visit_Name(self, node: nodes.Name, frame: Frame) -> None:
1591 if node.ctx == "store" and (
1592 frame.toplevel or frame.loop_frame or frame.block_frame
1593 ):
Takuto Ikuta9f3b732a2018-03-14 14:18:561594 if self._assign_stack:
1595 self._assign_stack[-1].add(node.name)
1596 ref = frame.symbols.ref(node.name)
1597
1598 # If we are looking up a variable we might have to deal with the
1599 # case where it's undefined. We can skip that case if the load
1600 # instruction indicates a parameter which are always defined.
Yuki Shiino57cb49722021-10-06 10:14:391601 if node.ctx == "load":
Takuto Ikuta9f3b732a2018-03-14 14:18:561602 load = frame.symbols.find_load(ref)
Yuki Shiino57cb49722021-10-06 10:14:391603 if not (
1604 load is not None
1605 and load[0] == VAR_LOAD_PARAMETER
1606 and not self.parameter_is_undeclared(ref)
1607 ):
1608 self.write(
Yuki Shiino2aec0732022-12-28 03:54:561609 f"(undefined(name={node.name!r}) if {ref} is missing else {ref})"
Yuki Shiino57cb49722021-10-06 10:14:391610 )
Takuto Ikuta9f3b732a2018-03-14 14:18:561611 return
1612
1613 self.write(ref)
1614
Yuki Shiino2aec0732022-12-28 03:54:561615 def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561616 # NSRefs can only be used to store values; since they use the normal
1617 # `foo.bar` notation they will be parsed as a normal attribute access
1618 # when used anywhere but in a `set` context
1619 ref = frame.symbols.ref(node.name)
Yuki Shiino2aec0732022-12-28 03:54:561620 self.writeline(f"if not isinstance({ref}, Namespace):")
Takuto Ikuta9f3b732a2018-03-14 14:18:561621 self.indent()
Yuki Shiino57cb49722021-10-06 10:14:391622 self.writeline(
Yuki Shiino2aec0732022-12-28 03:54:561623 "raise TemplateRuntimeError"
1624 '("cannot assign attribute on non-namespace object")'
Yuki Shiino57cb49722021-10-06 10:14:391625 )
Takuto Ikuta9f3b732a2018-03-14 14:18:561626 self.outdent()
Yuki Shiino2aec0732022-12-28 03:54:561627 self.writeline(f"{ref}[{node.attr!r}]")
[email protected]88a0c562013-05-09 11:33:471628
Yuki Shiino2aec0732022-12-28 03:54:561629 def visit_Const(self, node: nodes.Const, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561630 val = node.as_const(frame.eval_ctx)
[email protected]88a0c562013-05-09 11:33:471631 if isinstance(val, float):
1632 self.write(str(val))
1633 else:
1634 self.write(repr(val))
1635
Yuki Shiino2aec0732022-12-28 03:54:561636 def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471637 try:
1638 self.write(repr(node.as_const(frame.eval_ctx)))
1639 except nodes.Impossible:
Yuki Shiino57cb49722021-10-06 10:14:391640 self.write(
Yuki Shiino2aec0732022-12-28 03:54:561641 f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})"
Yuki Shiino57cb49722021-10-06 10:14:391642 )
[email protected]88a0c562013-05-09 11:33:471643
Yuki Shiino2aec0732022-12-28 03:54:561644 def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391645 self.write("(")
[email protected]88a0c562013-05-09 11:33:471646 idx = -1
1647 for idx, item in enumerate(node.items):
1648 if idx:
Yuki Shiino57cb49722021-10-06 10:14:391649 self.write(", ")
[email protected]88a0c562013-05-09 11:33:471650 self.visit(item, frame)
Yuki Shiino2aec0732022-12-28 03:54:561651 self.write(",)" if idx == 0 else ")")
[email protected]88a0c562013-05-09 11:33:471652
Yuki Shiino2aec0732022-12-28 03:54:561653 def visit_List(self, node: nodes.List, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391654 self.write("[")
[email protected]88a0c562013-05-09 11:33:471655 for idx, item in enumerate(node.items):
1656 if idx:
Yuki Shiino57cb49722021-10-06 10:14:391657 self.write(", ")
[email protected]88a0c562013-05-09 11:33:471658 self.visit(item, frame)
Yuki Shiino57cb49722021-10-06 10:14:391659 self.write("]")
[email protected]88a0c562013-05-09 11:33:471660
Yuki Shiino2aec0732022-12-28 03:54:561661 def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391662 self.write("{")
[email protected]88a0c562013-05-09 11:33:471663 for idx, item in enumerate(node.items):
1664 if idx:
Yuki Shiino57cb49722021-10-06 10:14:391665 self.write(", ")
[email protected]88a0c562013-05-09 11:33:471666 self.visit(item.key, frame)
Yuki Shiino57cb49722021-10-06 10:14:391667 self.write(": ")
[email protected]88a0c562013-05-09 11:33:471668 self.visit(item.value, frame)
Yuki Shiino57cb49722021-10-06 10:14:391669 self.write("}")
[email protected]88a0c562013-05-09 11:33:471670
Yuki Shiino2aec0732022-12-28 03:54:561671 visit_Add = _make_binop("+")
1672 visit_Sub = _make_binop("-")
1673 visit_Mul = _make_binop("*")
1674 visit_Div = _make_binop("/")
1675 visit_FloorDiv = _make_binop("//")
1676 visit_Pow = _make_binop("**")
1677 visit_Mod = _make_binop("%")
1678 visit_And = _make_binop("and")
1679 visit_Or = _make_binop("or")
1680 visit_Pos = _make_unop("+")
1681 visit_Neg = _make_unop("-")
1682 visit_Not = _make_unop("not ")
[email protected]88a0c562013-05-09 11:33:471683
Takuto Ikuta9f3b732a2018-03-14 14:18:561684 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561685 def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471686 if frame.eval_ctx.volatile:
Yuki Shiino2aec0732022-12-28 03:54:561687 func_name = "(markup_join if context.eval_ctx.volatile else str_join)"
[email protected]88a0c562013-05-09 11:33:471688 elif frame.eval_ctx.autoescape:
Yuki Shiino57cb49722021-10-06 10:14:391689 func_name = "markup_join"
[email protected]88a0c562013-05-09 11:33:471690 else:
Yuki Shiino2aec0732022-12-28 03:54:561691 func_name = "str_join"
1692 self.write(f"{func_name}((")
[email protected]88a0c562013-05-09 11:33:471693 for arg in node.nodes:
1694 self.visit(arg, frame)
Yuki Shiino57cb49722021-10-06 10:14:391695 self.write(", ")
1696 self.write("))")
[email protected]88a0c562013-05-09 11:33:471697
Takuto Ikuta9f3b732a2018-03-14 14:18:561698 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561699 def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391700 self.write("(")
[email protected]88a0c562013-05-09 11:33:471701 self.visit(node.expr, frame)
1702 for op in node.ops:
1703 self.visit(op, frame)
Yuki Shiino57cb49722021-10-06 10:14:391704 self.write(")")
[email protected]88a0c562013-05-09 11:33:471705
Yuki Shiino2aec0732022-12-28 03:54:561706 def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None:
1707 self.write(f" {operators[node.op]} ")
[email protected]88a0c562013-05-09 11:33:471708 self.visit(node.expr, frame)
1709
Takuto Ikuta9f3b732a2018-03-14 14:18:561710 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561711 def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391712 if self.environment.is_async:
1713 self.write("(await auto_await(")
1714
1715 self.write("environment.getattr(")
[email protected]88a0c562013-05-09 11:33:471716 self.visit(node.node, frame)
Yuki Shiino2aec0732022-12-28 03:54:561717 self.write(f", {node.attr!r})")
Yuki Shiino57cb49722021-10-06 10:14:391718
1719 if self.environment.is_async:
1720 self.write("))")
[email protected]88a0c562013-05-09 11:33:471721
Takuto Ikuta9f3b732a2018-03-14 14:18:561722 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561723 def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471724 # slices bypass the environment getitem method.
1725 if isinstance(node.arg, nodes.Slice):
1726 self.visit(node.node, frame)
Yuki Shiino57cb49722021-10-06 10:14:391727 self.write("[")
[email protected]88a0c562013-05-09 11:33:471728 self.visit(node.arg, frame)
Yuki Shiino57cb49722021-10-06 10:14:391729 self.write("]")
[email protected]88a0c562013-05-09 11:33:471730 else:
Yuki Shiino57cb49722021-10-06 10:14:391731 if self.environment.is_async:
1732 self.write("(await auto_await(")
1733
1734 self.write("environment.getitem(")
[email protected]88a0c562013-05-09 11:33:471735 self.visit(node.node, frame)
Yuki Shiino57cb49722021-10-06 10:14:391736 self.write(", ")
[email protected]88a0c562013-05-09 11:33:471737 self.visit(node.arg, frame)
Yuki Shiino57cb49722021-10-06 10:14:391738 self.write(")")
1739
1740 if self.environment.is_async:
1741 self.write("))")
[email protected]88a0c562013-05-09 11:33:471742
Yuki Shiino2aec0732022-12-28 03:54:561743 def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471744 if node.start is not None:
1745 self.visit(node.start, frame)
Yuki Shiino57cb49722021-10-06 10:14:391746 self.write(":")
[email protected]88a0c562013-05-09 11:33:471747 if node.stop is not None:
1748 self.visit(node.stop, frame)
1749 if node.step is not None:
Yuki Shiino57cb49722021-10-06 10:14:391750 self.write(":")
[email protected]88a0c562013-05-09 11:33:471751 self.visit(node.step, frame)
1752
Yuki Shiino2aec0732022-12-28 03:54:561753 @contextmanager
1754 def _filter_test_common(
1755 self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool
1756 ) -> t.Iterator[None]:
Takuto Ikuta9f3b732a2018-03-14 14:18:561757 if self.environment.is_async:
Yuki Shiino2aec0732022-12-28 03:54:561758 self.write("(await auto_await(")
[email protected]88a0c562013-05-09 11:33:471759
Yuki Shiino2aec0732022-12-28 03:54:561760 if is_filter:
1761 self.write(f"{self.filters[node.name]}(")
1762 func = self.environment.filters.get(node.name)
[email protected]88a0c562013-05-09 11:33:471763 else:
Yuki Shiino2aec0732022-12-28 03:54:561764 self.write(f"{self.tests[node.name]}(")
1765 func = self.environment.tests.get(node.name)
1766
1767 # When inside an If or CondExpr frame, allow the filter to be
1768 # undefined at compile time and only raise an error if it's
1769 # actually called at runtime. See pull_dependencies.
1770 if func is None and not frame.soft_frame:
1771 type_name = "filter" if is_filter else "test"
1772 self.fail(f"No {type_name} named {node.name!r}.", node.lineno)
1773
1774 pass_arg = {
1775 _PassArg.context: "context",
1776 _PassArg.eval_context: "context.eval_ctx",
1777 _PassArg.environment: "environment",
1778 }.get(
1779 _PassArg.from_obj(func) # type: ignore
1780 )
1781
1782 if pass_arg is not None:
1783 self.write(f"{pass_arg}, ")
1784
1785 # Back to the visitor function to handle visiting the target of
1786 # the filter or test.
1787 yield
1788
[email protected]88a0c562013-05-09 11:33:471789 self.signature(node, frame)
Yuki Shiino57cb49722021-10-06 10:14:391790 self.write(")")
Yuki Shiino2aec0732022-12-28 03:54:561791
Takuto Ikuta9f3b732a2018-03-14 14:18:561792 if self.environment.is_async:
Yuki Shiino2aec0732022-12-28 03:54:561793 self.write("))")
[email protected]88a0c562013-05-09 11:33:471794
Takuto Ikuta9f3b732a2018-03-14 14:18:561795 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561796 def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None:
1797 with self._filter_test_common(node, frame, True):
1798 # if the filter node is None we are inside a filter block
1799 # and want to write to the current buffer
1800 if node.node is not None:
1801 self.visit(node.node, frame)
1802 elif frame.eval_ctx.volatile:
1803 self.write(
1804 f"(Markup(concat({frame.buffer}))"
1805 f" if context.eval_ctx.autoescape else concat({frame.buffer}))"
Yuki Shiino57cb49722021-10-06 10:14:391806 )
Yuki Shiino2aec0732022-12-28 03:54:561807 elif frame.eval_ctx.autoescape:
1808 self.write(f"Markup(concat({frame.buffer}))")
1809 else:
1810 self.write(f"concat({frame.buffer})")
1811
1812 @optimizeconst
1813 def visit_Test(self, node: nodes.Test, frame: Frame) -> None:
1814 with self._filter_test_common(node, frame, False):
1815 self.visit(node.node, frame)
1816
1817 @optimizeconst
1818 def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None:
1819 frame = frame.soft()
1820
1821 def write_expr2() -> None:
1822 if node.expr2 is not None:
1823 self.visit(node.expr2, frame)
1824 return
1825
1826 self.write(
1827 f'cond_expr_undefined("the inline if-expression on'
1828 f" {self.position(node)} evaluated to false and no else"
1829 f' section was defined.")'
Yuki Shiino57cb49722021-10-06 10:14:391830 )
[email protected]88a0c562013-05-09 11:33:471831
Yuki Shiino57cb49722021-10-06 10:14:391832 self.write("(")
[email protected]e6e05702013-09-06 09:48:341833 self.visit(node.expr1, frame)
Yuki Shiino57cb49722021-10-06 10:14:391834 self.write(" if ")
[email protected]e6e05702013-09-06 09:48:341835 self.visit(node.test, frame)
Yuki Shiino57cb49722021-10-06 10:14:391836 self.write(" else ")
[email protected]e6e05702013-09-06 09:48:341837 write_expr2()
Yuki Shiino57cb49722021-10-06 10:14:391838 self.write(")")
[email protected]88a0c562013-05-09 11:33:471839
Takuto Ikuta9f3b732a2018-03-14 14:18:561840 @optimizeconst
Yuki Shiino2aec0732022-12-28 03:54:561841 def visit_Call(
1842 self, node: nodes.Call, frame: Frame, forward_caller: bool = False
1843 ) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561844 if self.environment.is_async:
Yuki Shiino2aec0732022-12-28 03:54:561845 self.write("(await auto_await(")
[email protected]88a0c562013-05-09 11:33:471846 if self.environment.sandboxed:
Yuki Shiino57cb49722021-10-06 10:14:391847 self.write("environment.call(context, ")
[email protected]88a0c562013-05-09 11:33:471848 else:
Yuki Shiino57cb49722021-10-06 10:14:391849 self.write("context.call(")
[email protected]88a0c562013-05-09 11:33:471850 self.visit(node.node, frame)
Yuki Shiino2aec0732022-12-28 03:54:561851 extra_kwargs = {"caller": "caller"} if forward_caller else None
1852 loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {}
1853 block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {}
1854 if extra_kwargs:
1855 extra_kwargs.update(loop_kwargs, **block_kwargs)
1856 elif loop_kwargs or block_kwargs:
1857 extra_kwargs = dict(loop_kwargs, **block_kwargs)
[email protected]88a0c562013-05-09 11:33:471858 self.signature(node, frame, extra_kwargs)
Yuki Shiino57cb49722021-10-06 10:14:391859 self.write(")")
Takuto Ikuta9f3b732a2018-03-14 14:18:561860 if self.environment.is_async:
Yuki Shiino2aec0732022-12-28 03:54:561861 self.write("))")
[email protected]88a0c562013-05-09 11:33:471862
Yuki Shiino2aec0732022-12-28 03:54:561863 def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391864 self.write(node.key + "=")
[email protected]88a0c562013-05-09 11:33:471865 self.visit(node.value, frame)
1866
1867 # -- Unused nodes for extensions
1868
Yuki Shiino2aec0732022-12-28 03:54:561869 def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391870 self.write("Markup(")
[email protected]88a0c562013-05-09 11:33:471871 self.visit(node.expr, frame)
Yuki Shiino57cb49722021-10-06 10:14:391872 self.write(")")
[email protected]88a0c562013-05-09 11:33:471873
Yuki Shiino2aec0732022-12-28 03:54:561874 def visit_MarkSafeIfAutoescape(
1875 self, node: nodes.MarkSafeIfAutoescape, frame: Frame
1876 ) -> None:
1877 self.write("(Markup if context.eval_ctx.autoescape else identity)(")
[email protected]88a0c562013-05-09 11:33:471878 self.visit(node.expr, frame)
Yuki Shiino57cb49722021-10-06 10:14:391879 self.write(")")
[email protected]88a0c562013-05-09 11:33:471880
Yuki Shiino2aec0732022-12-28 03:54:561881 def visit_EnvironmentAttribute(
1882 self, node: nodes.EnvironmentAttribute, frame: Frame
1883 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391884 self.write("environment." + node.name)
[email protected]88a0c562013-05-09 11:33:471885
Yuki Shiino2aec0732022-12-28 03:54:561886 def visit_ExtensionAttribute(
1887 self, node: nodes.ExtensionAttribute, frame: Frame
1888 ) -> None:
1889 self.write(f"environment.extensions[{node.identifier!r}].{node.name}")
[email protected]88a0c562013-05-09 11:33:471890
Yuki Shiino2aec0732022-12-28 03:54:561891 def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471892 self.write(self.import_aliases[node.importname])
1893
Yuki Shiino2aec0732022-12-28 03:54:561894 def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471895 self.write(node.name)
1896
Yuki Shiino2aec0732022-12-28 03:54:561897 def visit_ContextReference(
1898 self, node: nodes.ContextReference, frame: Frame
1899 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391900 self.write("context")
1901
Yuki Shiino2aec0732022-12-28 03:54:561902 def visit_DerivedContextReference(
1903 self, node: nodes.DerivedContextReference, frame: Frame
1904 ) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391905 self.write(self.derive_context(frame))
[email protected]88a0c562013-05-09 11:33:471906
Yuki Shiino2aec0732022-12-28 03:54:561907 def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391908 self.writeline("continue", node)
[email protected]88a0c562013-05-09 11:33:471909
Yuki Shiino2aec0732022-12-28 03:54:561910 def visit_Break(self, node: nodes.Break, frame: Frame) -> None:
Yuki Shiino57cb49722021-10-06 10:14:391911 self.writeline("break", node)
[email protected]88a0c562013-05-09 11:33:471912
Yuki Shiino2aec0732022-12-28 03:54:561913 def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None:
[email protected]88a0c562013-05-09 11:33:471914 scope_frame = frame.inner()
Takuto Ikuta9f3b732a2018-03-14 14:18:561915 scope_frame.symbols.analyze_node(node)
1916 self.enter_frame(scope_frame)
[email protected]88a0c562013-05-09 11:33:471917 self.blockvisit(node.body, scope_frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561918 self.leave_frame(scope_frame)
1919
Yuki Shiino2aec0732022-12-28 03:54:561920 def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None:
Takuto Ikuta9f3b732a2018-03-14 14:18:561921 ctx = self.temporary_identifier()
Yuki Shiino2aec0732022-12-28 03:54:561922 self.writeline(f"{ctx} = {self.derive_context(frame)}")
1923 self.writeline(f"{ctx}.vars = ")
Takuto Ikuta9f3b732a2018-03-14 14:18:561924 self.visit(node.context, frame)
1925 self.push_context_reference(ctx)
1926
1927 scope_frame = frame.inner(isolated=True)
1928 scope_frame.symbols.analyze_node(node)
1929 self.enter_frame(scope_frame)
1930 self.blockvisit(node.body, scope_frame)
1931 self.leave_frame(scope_frame)
1932 self.pop_context_reference()
[email protected]88a0c562013-05-09 11:33:471933
Yuki Shiino2aec0732022-12-28 03:54:561934 def visit_EvalContextModifier(
1935 self, node: nodes.EvalContextModifier, frame: Frame
1936 ) -> None:
[email protected]88a0c562013-05-09 11:33:471937 for keyword in node.options:
Yuki Shiino2aec0732022-12-28 03:54:561938 self.writeline(f"context.eval_ctx.{keyword.key} = ")
[email protected]88a0c562013-05-09 11:33:471939 self.visit(keyword.value, frame)
1940 try:
1941 val = keyword.value.as_const(frame.eval_ctx)
1942 except nodes.Impossible:
1943 frame.eval_ctx.volatile = True
1944 else:
1945 setattr(frame.eval_ctx, keyword.key, val)
1946
Yuki Shiino2aec0732022-12-28 03:54:561947 def visit_ScopedEvalContextModifier(
1948 self, node: nodes.ScopedEvalContextModifier, frame: Frame
1949 ) -> None:
[email protected]88a0c562013-05-09 11:33:471950 old_ctx_name = self.temporary_identifier()
Takuto Ikuta9f3b732a2018-03-14 14:18:561951 saved_ctx = frame.eval_ctx.save()
Yuki Shiino2aec0732022-12-28 03:54:561952 self.writeline(f"{old_ctx_name} = context.eval_ctx.save()")
[email protected]88a0c562013-05-09 11:33:471953 self.visit_EvalContextModifier(node, frame)
1954 for child in node.body:
1955 self.visit(child, frame)
Takuto Ikuta9f3b732a2018-03-14 14:18:561956 frame.eval_ctx.revert(saved_ctx)
Yuki Shiino2aec0732022-12-28 03:54:561957 self.writeline(f"context.eval_ctx.revert({old_ctx_name})")