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