comparison env/lib/python3.7/site-packages/jinja2/compiler.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
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)