1# -*- coding: utf-8 -*-
2"""Compiles nodes from the parser into Python code."""
3from collections import namedtuple
4from functools import update_wrapper
5from itertools import chain
6from keyword import iskeyword as is_python_keyword
7
8from markupsafe import escape
9from markupsafe import Markup
10
11from . import nodes
12from ._compat import imap
13from ._compat import iteritems
14from ._compat import izip
15from ._compat import NativeStringIO
16from ._compat import range_type
17from ._compat import string_types
18from ._compat import text_type
19from .exceptions import TemplateAssertionError
20from .idtracking import Symbols
21from .idtracking import VAR_LOAD_ALIAS
22from .idtracking import VAR_LOAD_PARAMETER
23from .idtracking import VAR_LOAD_RESOLVE
24from .idtracking import VAR_LOAD_UNDEFINED
25from .nodes import EvalContext
26from .optimizer import Optimizer
27from .utils import concat
28from .visitor import NodeVisitor
29
30operators = {
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
43if hasattr(dict, "iteritems"):
44    dict_item_iter = "iteritems"
45else:
46    dict_item_iter = "items"
47
48code_features = ["division"]
49
50# does this python version support generator stops? (PEP 0479)
51try:
52    exec("from __future__ import generator_stop")
53    code_features.append("generator_stop")
54except SyntaxError:
55    pass
56
57# does this python version support yield from?
58try:
59    exec("def f(): yield from x()")
60except SyntaxError:
61    supports_yield_from = False
62else:
63    supports_yield_from = True
64
65
66def 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
78def 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
92def 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
113def 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
126class 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
134class 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
195class VisitorExit(RuntimeError):
196    """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
197
198
199class 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
218class 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
240class 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
247class 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)
1844