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