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