added "with context" or "without context" import/include modifiers
[jinja2.git] / jinja2 / compiler.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.compiler
4     ~~~~~~~~~~~~~~~
5
6     Compiles nodes into python code.
7
8     :copyright: Copyright 2008 by Armin Ronacher.
9     :license: GNU GPL.
10 """
11 from time import time
12 from copy import copy
13 from random import randrange
14 from keyword import iskeyword
15 from cStringIO import StringIO
16 from itertools import chain
17 from jinja2 import nodes
18 from jinja2.visitor import NodeVisitor, NodeTransformer
19 from jinja2.exceptions import TemplateAssertionError
20 from jinja2.runtime import concat
21 from jinja2.utils import Markup
22
23
24 operators = {
25     'eq':       '==',
26     'ne':       '!=',
27     'gt':       '>',
28     'gteq':     '>=',
29     'lt':       '<',
30     'lteq':     '<=',
31     'in':       'in',
32     'notin':    'not in'
33 }
34
35 try:
36     exec '(0 if 0 else 0)'
37 except SyntaxError:
38     have_condexpr = False
39 else:
40     have_condexpr = True
41
42
43 def generate(node, environment, name, filename, stream=None):
44     """Generate the python source for a node tree."""
45     generator = CodeGenerator(environment, name, filename, stream)
46     generator.visit(node)
47     if stream is None:
48         return generator.stream.getvalue()
49
50
51 def has_safe_repr(value):
52     """Does the node have a safe representation?"""
53     if value is None or value is NotImplemented or value is Ellipsis:
54         return True
55     if isinstance(value, (bool, int, long, float, complex, basestring,
56                           xrange, Markup)):
57         return True
58     if isinstance(value, (tuple, list, set, frozenset)):
59         for item in value:
60             if not has_safe_repr(item):
61                 return False
62         return True
63     elif isinstance(value, dict):
64         for key, value in value.iteritems():
65             if not has_safe_repr(key):
66                 return False
67             if not has_safe_repr(value):
68                 return False
69         return True
70     return False
71
72
73 def find_undeclared(nodes, names):
74     """Check if the names passed are accessed undeclared.  The return value
75     is a set of all the undeclared names from the sequence of names found.
76     """
77     visitor = UndeclaredNameVisitor(names)
78     try:
79         for node in nodes:
80             visitor.visit(node)
81     except VisitorExit:
82         pass
83     return visitor.undeclared
84
85
86 class Identifiers(object):
87     """Tracks the status of identifiers in frames."""
88
89     def __init__(self):
90         # variables that are known to be declared (probably from outer
91         # frames or because they are special for the frame)
92         self.declared = set()
93
94         # undeclared variables from outer scopes
95         self.outer_undeclared = set()
96
97         # names that are accessed without being explicitly declared by
98         # this one or any of the outer scopes.  Names can appear both in
99         # declared and undeclared.
100         self.undeclared = set()
101
102         # names that are declared locally
103         self.declared_locally = set()
104
105         # names that are declared by parameters
106         self.declared_parameter = set()
107
108     def add_special(self, name):
109         """Register a special name like `loop`."""
110         self.undeclared.discard(name)
111         self.declared.add(name)
112
113     def is_declared(self, name, local_only=False):
114         """Check if a name is declared in this or an outer scope."""
115         if name in self.declared_locally or name in self.declared_parameter:
116             return True
117         if local_only:
118             return False
119         return name in self.declared
120
121     def find_shadowed(self):
122         """Find all the shadowed names."""
123         return (self.declared | self.outer_undeclared) & \
124                (self.declared_locally | self.declared_parameter)
125
126
127 class Frame(object):
128     """Holds compile time information for us."""
129
130     def __init__(self, parent=None):
131         self.identifiers = Identifiers()
132
133         # a toplevel frame is the root + soft frames such as if conditions.
134         self.toplevel = False
135
136         # the root frame is basically just the outermost frame, so no if
137         # conditions.  This information is used to optimize inheritance
138         # situations.
139         self.rootlevel = False
140
141         # inside some tags we are using a buffer rather than yield statements.
142         # this for example affects {% filter %} or {% macro %}.  If a frame
143         # is buffered this variable points to the name of the list used as
144         # buffer.
145         self.buffer = None
146
147         # the name of the block we're in, otherwise None.
148         self.block = parent and parent.block or None
149
150         # the parent of this frame
151         self.parent = parent
152
153         if parent is not None:
154             self.identifiers.declared.update(
155                 parent.identifiers.declared |
156                 parent.identifiers.declared_locally |
157                 parent.identifiers.declared_parameter
158             )
159             self.identifiers.outer_undeclared.update(
160                 parent.identifiers.undeclared -
161                 self.identifiers.declared
162             )
163             self.buffer = parent.buffer
164
165     def copy(self):
166         """Create a copy of the current one."""
167         rv = copy(self)
168         rv.identifiers = copy(self.identifiers)
169         return rv
170
171     def inspect(self, nodes, hard_scope=False):
172         """Walk the node and check for identifiers.  If the scope is hard (eg:
173         enforce on a python level) overrides from outer scopes are tracked
174         differently.
175         """
176         visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
177         for node in nodes:
178             visitor.visit(node)
179
180     def inner(self):
181         """Return an inner frame."""
182         return Frame(self)
183
184     def soft(self):
185         """Return a soft frame.  A soft frame may not be modified as
186         standalone thing as it shares the resources with the frame it
187         was created of, but it's not a rootlevel frame any longer.
188         """
189         rv = copy(self)
190         rv.rootlevel = False
191         return rv
192
193
194 class VisitorExit(RuntimeError):
195     """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
196
197
198 class DependencyFinderVisitor(NodeVisitor):
199     """A visitor that collects filter and test calls."""
200
201     def __init__(self):
202         self.filters = set()
203         self.tests = set()
204
205     def visit_Filter(self, node):
206         self.generic_visit(node)
207         self.filters.add(node.name)
208
209     def visit_Test(self, node):
210         self.generic_visit(node)
211         self.tests.add(node.name)
212
213     def visit_Block(self, node):
214         """Stop visiting at blocks."""
215
216
217 class UndeclaredNameVisitor(NodeVisitor):
218     """A visitor that checks if a name is accessed without being
219     declared.  This is different from the frame visitor as it will
220     not stop at closure frames.
221     """
222
223     def __init__(self, names):
224         self.names = set(names)
225         self.undeclared = set()
226
227     def visit_Name(self, node):
228         if node.ctx == 'load' and node.name in self.names:
229             self.undeclared.add(node.name)
230             if self.undeclared == self.names:
231                 raise VisitorExit()
232         else:
233             self.names.discard(node.name)
234
235     def visit_Block(self, node):
236         """Stop visiting a blocks."""
237
238
239 class FrameIdentifierVisitor(NodeVisitor):
240     """A visitor for `Frame.inspect`."""
241
242     def __init__(self, identifiers, hard_scope):
243         self.identifiers = identifiers
244         self.hard_scope = hard_scope
245
246     def visit_Name(self, node):
247         """All assignments to names go through this function."""
248         if node.ctx in ('store', 'param'):
249             self.identifiers.declared_locally.add(node.name)
250         elif node.ctx == 'load' and not \
251              self.identifiers.is_declared(node.name, self.hard_scope):
252             self.identifiers.undeclared.add(node.name)
253
254     def visit_Macro(self, node):
255         self.generic_visit(node)
256         self.identifiers.declared_locally.add(node.name)
257
258     def visit_Import(self, node):
259         self.generic_visit(node)
260         self.identifiers.declared_locally.add(node.target)
261
262     def visit_FromImport(self, node):
263         self.generic_visit(node)
264         for name in node.names:
265             if isinstance(name, tuple):
266                 self.identifiers.declared_locally.add(name[1])
267             else:
268                 self.identifiers.declared_locally.add(name)
269
270     def visit_Assign(self, node):
271         """Visit assignments in the correct order."""
272         self.visit(node.node)
273         self.visit(node.target)
274
275     def visit_For(self, node):
276         """Visiting stops at for blocks.  However the block sequence
277         is visited as part of the outer scope.
278         """
279         self.visit(node.iter)
280
281     def visit_CallBlock(self, node):
282         for child in node.iter_child_nodes(exclude=('body',)):
283             self.visit(child)
284
285     def visit_FilterBlock(self, node):
286         self.visit(node.filter)
287
288     def visit_Block(self, node):
289         """Stop visiting at blocks."""
290
291
292 class CompilerExit(Exception):
293     """Raised if the compiler encountered a situation where it just
294     doesn't make sense to further process the code.  Any block that
295     raises such an exception is not further processed.
296     """
297
298
299 class CodeGenerator(NodeVisitor):
300
301     def __init__(self, environment, name, filename, stream=None):
302         if stream is None:
303             stream = StringIO()
304         self.environment = environment
305         self.name = name
306         self.filename = filename
307         self.stream = stream
308
309         # a registry for all blocks.  Because blocks are moved out
310         # into the global python scope they are registered here
311         self.blocks = {}
312
313         # the number of extends statements so far
314         self.extends_so_far = 0
315
316         # some templates have a rootlevel extends.  In this case we
317         # can safely assume that we're a child template and do some
318         # more optimizations.
319         self.has_known_extends = False
320
321         # the current line number
322         self.code_lineno = 1
323
324         # the debug information
325         self.debug_info = []
326         self._write_debug_info = None
327
328         # the number of new lines before the next write()
329         self._new_lines = 0
330
331         # the line number of the last written statement
332         self._last_line = 0
333
334         # true if nothing was written so far.
335         self._first_write = True
336
337         # used by the `temporary_identifier` method to get new
338         # unique, temporary identifier
339         self._last_identifier = 0
340
341         # the current indentation
342         self._indentation = 0
343
344     def temporary_identifier(self):
345         """Get a new unique identifier."""
346         self._last_identifier += 1
347         return 't%d' % self._last_identifier
348
349     def indent(self):
350         """Indent by one."""
351         self._indentation += 1
352
353     def outdent(self, step=1):
354         """Outdent by step."""
355         self._indentation -= step
356
357     def blockvisit(self, nodes, frame, force_generator=True):
358         """Visit a list of nodes as block in a frame.  If the current frame
359         is no buffer a dummy ``if 0: yield None`` is written automatically
360         unless the force_generator parameter is set to False.
361         """
362         if frame.buffer is None and force_generator:
363             self.writeline('if 0: yield None')
364         try:
365             for node in nodes:
366                 self.visit(node, frame)
367         except CompilerExit:
368             pass
369
370     def write(self, x):
371         """Write a string into the output stream."""
372         if self._new_lines:
373             if not self._first_write:
374                 self.stream.write('\n' * self._new_lines)
375                 self.code_lineno += self._new_lines
376                 if self._write_debug_info is not None:
377                     self.debug_info.append((self._write_debug_info,
378                                             self.code_lineno))
379                     self._write_debug_info = None
380             self._first_write = False
381             self.stream.write('    ' * self._indentation)
382             self._new_lines = 0
383         self.stream.write(x)
384
385     def writeline(self, x, node=None, extra=0):
386         """Combination of newline and write."""
387         self.newline(node, extra)
388         self.write(x)
389
390     def newline(self, node=None, extra=0):
391         """Add one or more newlines before the next write."""
392         self._new_lines = max(self._new_lines, 1 + extra)
393         if node is not None and node.lineno != self._last_line:
394             self._write_debug_info = node.lineno
395             self._last_line = node.lineno
396
397     def signature(self, node, frame, have_comma=True, extra_kwargs=None):
398         """Writes a function call to the stream for the current node.
399         Per default it will write a leading comma but this can be
400         disabled by setting have_comma to False.  The extra keyword
401         arguments may not include python keywords otherwise a syntax
402         error could occour.  The extra keyword arguments should be given
403         as python dict.
404         """
405         have_comma = have_comma and [True] or []
406         def touch_comma():
407             if have_comma:
408                 self.write(', ')
409             else:
410                 have_comma.append(True)
411
412         # if any of the given keyword arguments is a python keyword
413         # we have to make sure that no invalid call is created.
414         kwarg_workaround = False
415         for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
416             if iskeyword(kwarg):
417                 kwarg_workaround = True
418                 break
419
420         for arg in node.args:
421             touch_comma()
422             self.visit(arg, frame)
423
424         if not kwarg_workaround:
425             for kwarg in node.kwargs:
426                 touch_comma()
427                 self.visit(kwarg, frame)
428             if extra_kwargs is not None:
429                 for key, value in extra_kwargs.iteritems():
430                     touch_comma()
431                     self.write('%s=%s' % (key, value))
432         if node.dyn_args:
433             touch_comma()
434             self.write('*')
435             self.visit(node.dyn_args, frame)
436
437         if kwarg_workaround:
438             touch_comma()
439             if node.dyn_kwargs is not None:
440                 self.write('**dict({')
441             else:
442                 self.write('**{')
443             for kwarg in node.kwargs:
444                 self.write('%r: ' % kwarg.key)
445                 self.visit(kwarg.value, frame)
446                 self.write(', ')
447             if extra_kwargs is not None:
448                 for key, value in extra_kwargs.iteritems():
449                     self.write('%r: %s, ' % (key, value))
450             if node.dyn_kwargs is not None:
451                 self.write('}, **')
452                 self.visit(node.dyn_kwargs, frame)
453                 self.write(')')
454             else:
455                 self.write('}')
456
457         elif node.dyn_kwargs is not None:
458             touch_comma()
459             self.write('**')
460             self.visit(node.dyn_kwargs, frame)
461
462     def pull_locals(self, frame):
463         """Pull all the references identifiers into the local scope."""
464         for name in frame.identifiers.undeclared:
465             self.writeline('l_%s = context[%r]' % (name, name))
466
467     def pull_dependencies(self, nodes):
468         """Pull all the dependencies."""
469         visitor = DependencyFinderVisitor()
470         for node in nodes:
471             visitor.visit(node)
472         for name in visitor.filters:
473             self.writeline('f_%s = environment.filters[%r]' % (name, name))
474         for name in visitor.tests:
475             self.writeline('t_%s = environment.tests[%r]' % (name, name))
476
477     def collect_shadowed(self, frame):
478         """This function returns all the shadowed variables in a dict
479         in the form name: alias and will write the required assignments
480         into the current scope.  No indentation takes place.
481         """
482         # make sure we "backup" overridden, local identifiers
483         # TODO: we should probably optimize this and check if the
484         # identifier is in use afterwards.
485         aliases = {}
486         for name in frame.identifiers.find_shadowed():
487             aliases[name] = ident = self.temporary_identifier()
488             self.writeline('%s = l_%s' % (ident, name))
489         return aliases
490
491     def function_scoping(self, node, frame, children=None):
492         """In Jinja a few statements require the help of anonymous
493         functions.  Those are currently macros and call blocks and in
494         the future also recursive loops.  As there is currently
495         technical limitation that doesn't allow reading and writing a
496         variable in a scope where the initial value is coming from an
497         outer scope, this function tries to fall back with a common
498         error message.  Additionally the frame passed is modified so
499         that the argumetns are collected and callers are looked up.
500
501         This will return the modified frame.
502         """
503         # we have to iterate twice over it, make sure that works
504         if children is None:
505             children = node.iter_child_nodes()
506         children = list(children)
507         func_frame = frame.inner()
508         func_frame.inspect(children, hard_scope=True)
509
510         # variables that are undeclared (accessed before declaration) and
511         # declared locally *and* part of an outside scope raise a template
512         # assertion error. Reason: we can't generate reasonable code from
513         # it without aliasing all the variables.  XXX: alias them ^^
514         overriden_closure_vars = (
515             func_frame.identifiers.undeclared &
516             func_frame.identifiers.declared &
517             (func_frame.identifiers.declared_locally |
518              func_frame.identifiers.declared_parameter)
519         )
520         if overriden_closure_vars:
521             vars = ', '.join(sorted(overriden_closure_vars))
522             raise TemplateAssertionError('It\'s not possible to set and '
523                                          'access variables derived from '
524                                          'an outer scope! (affects: %s' %
525                                          vars, node.lineno, self.name)
526
527         # remove variables from a closure from the frame's undeclared
528         # identifiers.
529         func_frame.identifiers.undeclared -= (
530             func_frame.identifiers.undeclared &
531             func_frame.identifiers.declared
532         )
533
534         func_frame.accesses_kwargs = False
535         func_frame.accesses_varargs = False
536         func_frame.accesses_caller = False
537         func_frame.arguments = args = ['l_' + x.name for x in node.args]
538
539         undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
540
541         if 'caller' in undeclared:
542             func_frame.accesses_caller = True
543             func_frame.identifiers.add_special('caller')
544             args.append('l_caller')
545         if 'kwargs' in undeclared:
546             func_frame.accesses_kwargs = True
547             func_frame.identifiers.add_special('kwargs')
548             args.append('l_kwargs')
549         if 'varargs' in undeclared:
550             func_frame.accesses_varargs = True
551             func_frame.identifiers.add_special('varargs')
552             args.append('l_varargs')
553         return func_frame
554
555     # -- Visitors
556
557     def visit_Template(self, node, frame=None):
558         assert frame is None, 'no root frame allowed'
559         from jinja2.runtime import __all__ as exported
560         self.writeline('from __future__ import division')
561         self.writeline('from jinja2.runtime import ' + ', '.join(exported))
562         self.writeline('name = %r' % self.name)
563
564         # do we have an extends tag at all?  If not, we can save some
565         # overhead by just not processing any inheritance code.
566         have_extends = node.find(nodes.Extends) is not None
567
568         # find all blocks
569         for block in node.find_all(nodes.Block):
570             if block.name in self.blocks:
571                 raise TemplateAssertionError('block %r defined twice' %
572                                              block.name, block.lineno,
573                                              self.name)
574             self.blocks[block.name] = block
575
576         # generate the root render function.
577         self.writeline('def root(context, environment=environment):', extra=1)
578
579         # process the root
580         frame = Frame()
581         frame.inspect(node.body)
582         frame.toplevel = frame.rootlevel = True
583         self.indent()
584         if have_extends:
585             self.writeline('parent_template = None')
586         self.pull_locals(frame)
587         self.pull_dependencies(node.body)
588         if 'self' in find_undeclared(node.body, ('self',)):
589             frame.identifiers.add_special('self')
590             self.writeline('l_self = TemplateReference(context)')
591         self.blockvisit(node.body, frame)
592         self.outdent()
593
594         # make sure that the parent root is called.
595         if have_extends:
596             if not self.has_known_extends:
597                 self.indent()
598                 self.writeline('if parent_template is not None:')
599             self.indent()
600             self.writeline('for event in parent_template.'
601                            'root_render_func(context):')
602             self.indent()
603             self.writeline('yield event')
604             self.outdent(2 + (not self.has_known_extends))
605
606         # at this point we now have the blocks collected and can visit them too.
607         for name, block in self.blocks.iteritems():
608             block_frame = Frame()
609             block_frame.inspect(block.body)
610             block_frame.block = name
611             self.writeline('def block_%s(context, environment=environment):'
612                            % name, block, 1)
613             self.indent()
614             undeclared = find_undeclared(block.body, ('self', 'super'))
615             if 'self' in undeclared:
616                 block_frame.identifiers.add_special('self')
617                 self.writeline('l_self = TemplateReference(context)')
618             if 'super' in undeclared:
619                 block_frame.identifiers.add_special('super')
620                 self.writeline('l_super = context.super(%r, '
621                                'block_%s)' % (name, name))
622             self.pull_locals(block_frame)
623             self.pull_dependencies(block.body)
624             self.blockvisit(block.body, block_frame)
625             self.outdent()
626
627         self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
628                                                    for x in self.blocks),
629                        extra=1)
630
631         # add a function that returns the debug info
632         self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
633                                                     in self.debug_info))
634
635     def visit_Block(self, node, frame):
636         """Call a block and register it for the template."""
637         level = 1
638         if frame.toplevel:
639             # if we know that we are a child template, there is no need to
640             # check if we are one
641             if self.has_known_extends:
642                 return
643             if self.extends_so_far > 0:
644                 self.writeline('if parent_template is None:')
645                 self.indent()
646                 level += 1
647         self.writeline('for event in context.blocks[%r][-1](context):' %
648                        node.name, node)
649         self.indent()
650         if frame.buffer is None:
651             self.writeline('yield event')
652         else:
653             self.writeline('%s.append(event)' % frame.buffer)
654         self.outdent(level)
655
656     def visit_Extends(self, node, frame):
657         """Calls the extender."""
658         if not frame.toplevel:
659             raise TemplateAssertionError('cannot use extend from a non '
660                                          'top-level scope', node.lineno,
661                                          self.name)
662
663         # if the number of extends statements in general is zero so
664         # far, we don't have to add a check if something extended
665         # the template before this one.
666         if self.extends_so_far > 0:
667
668             # if we have a known extends we just add a template runtime
669             # error into the generated code.  We could catch that at compile
670             # time too, but i welcome it not to confuse users by throwing the
671             # same error at different times just "because we can".
672             if not self.has_known_extends:
673                 self.writeline('if parent_template is not None:')
674                 self.indent()
675             self.writeline('raise TemplateRuntimeError(%r)' %
676                            'extended multiple times')
677
678             # if we have a known extends already we don't need that code here
679             # as we know that the template execution will end here.
680             if self.has_known_extends:
681                 raise CompilerExit()
682             self.outdent()
683
684         self.writeline('parent_template = environment.get_template(', node, 1)
685         self.visit(node.template, frame)
686         self.write(', %r)' % self.name)
687         self.writeline('for name, parent_block in parent_template.'
688                        'blocks.iteritems():')
689         self.indent()
690         self.writeline('context.blocks.setdefault(name, []).'
691                        'insert(0, parent_block)')
692         self.outdent()
693
694         # if this extends statement was in the root level we can take
695         # advantage of that information and simplify the generated code
696         # in the top level from this point onwards
697         self.has_known_extends = True
698
699         # and now we have one more
700         self.extends_so_far += 1
701
702     def visit_Include(self, node, frame):
703         """Handles includes."""
704         if node.with_context:
705             self.writeline('template = environment.get_template(', node)
706             self.visit(node.template, frame)
707             self.write(', %r)' % self.name)
708             self.writeline('for event in template.root_render_func('
709                            'template.new_context(context.parent, True)):')
710         else:
711             self.writeline('for event in environment.get_template(', node)
712             self.visit(node.template, frame)
713             self.write(', %r).module._TemplateModule__body_stream:' %
714                        self.name)
715         self.indent()
716         if frame.buffer is None:
717             self.writeline('yield event')
718         else:
719             self.writeline('%s.append(event)' % frame.buffer)
720         self.outdent()
721
722     def visit_Import(self, node, frame):
723         """Visit regular imports."""
724         self.writeline('l_%s = ' % node.target, node)
725         if frame.toplevel:
726             self.write('context.vars[%r] = ' % node.target)
727         self.write('environment.get_template(')
728         self.visit(node.template, frame)
729         self.write(', %r).' % self.name)
730         if node.with_context:
731             self.write('make_module(context.parent, True)')
732         else:
733             self.write('module')
734         if frame.toplevel and not node.target.startswith('__'):
735             self.writeline('context.exported_vars.discard(%r)' % node.target)
736
737     def visit_FromImport(self, node, frame):
738         """Visit named imports."""
739         self.newline(node)
740         self.write('included_template = environment.get_template(')
741         self.visit(node.template, frame)
742         self.write(', %r).' % self.name)
743         if node.with_context:
744             self.write('make_module(context.parent, True)')
745         else:
746             self.write('module')
747         for name in node.names:
748             if isinstance(name, tuple):
749                 name, alias = name
750             else:
751                 alias = name
752             self.writeline('l_%s = getattr(included_template, '
753                            '%r, missing)' % (alias, name))
754             self.writeline('if l_%s is missing:' % alias)
755             self.indent()
756             self.writeline('l_%s = environment.undefined(%r %% '
757                            'included_template.name, '
758                            'name=included_template.name)' %
759                            (alias, 'the template %r does not export '
760                             'the requested name ' + repr(name)))
761             self.outdent()
762             if frame.toplevel:
763                 self.writeline('context.vars[%r] = l_%s' % (alias, alias))
764                 if not alias.startswith('__'):
765                     self.writeline('context.exported_vars.discard(%r)' % alias)
766
767     def visit_For(self, node, frame):
768         loop_frame = frame.inner()
769         loop_frame.inspect(node.iter_child_nodes(exclude=('iter',)))
770         extended_loop = bool(node.else_) or \
771                         'loop' in loop_frame.identifiers.undeclared
772         if extended_loop:
773             loop_frame.identifiers.add_special('loop')
774
775         aliases = self.collect_shadowed(loop_frame)
776         self.pull_locals(loop_frame)
777         if node.else_:
778             self.writeline('l_loop = None')
779
780         self.newline(node)
781         self.writeline('for ')
782         self.visit(node.target, loop_frame)
783         self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
784
785         # the expression pointing to the parent loop.  We make the
786         # undefined a bit more debug friendly at the same time.
787         parent_loop = 'loop' in aliases and aliases['loop'] \
788                       or "environment.undefined(%r, name='loop')" % "'loop' " \
789                          'is undefined. "the filter section of a loop as well ' \
790                          'as the else block doesn\'t have access to the ' \
791                          "special 'loop' variable of the current loop.  " \
792                          "Because there is no parent loop it's undefined."
793
794         # if we have an extened loop and a node test, we filter in the
795         # "outer frame".
796         if extended_loop and node.test is not None:
797             self.write('(')
798             self.visit(node.target, loop_frame)
799             self.write(' for ')
800             self.visit(node.target, loop_frame)
801             self.write(' in ')
802             self.visit(node.iter, loop_frame)
803             self.write(' if (')
804             test_frame = loop_frame.copy()
805             self.writeline('l_loop = ' + parent_loop)
806             self.visit(node.test, test_frame)
807             self.write('))')
808
809         else:
810             self.visit(node.iter, loop_frame)
811
812         self.write(extended_loop and '):' or ':')
813
814         # tests in not extended loops become a continue
815         if not extended_loop and node.test is not None:
816             self.indent()
817             self.writeline('if ')
818             self.visit(node.test, loop_frame)
819             self.write(':')
820             self.indent()
821             self.writeline('continue')
822             self.outdent(2)
823
824         self.indent()
825         self.blockvisit(node.body, loop_frame, force_generator=True)
826         self.outdent()
827
828         if node.else_:
829             self.writeline('if l_loop is None:')
830             self.indent()
831             self.writeline('l_loop = ' + parent_loop)
832             self.blockvisit(node.else_, loop_frame, force_generator=False)
833             self.outdent()
834
835         # reset the aliases if there are any.
836         for name, alias in aliases.iteritems():
837             self.writeline('l_%s = %s' % (name, alias))
838
839     def visit_If(self, node, frame):
840         if_frame = frame.soft()
841         self.writeline('if ', node)
842         self.visit(node.test, if_frame)
843         self.write(':')
844         self.indent()
845         self.blockvisit(node.body, if_frame)
846         self.outdent()
847         if node.else_:
848             self.writeline('else:')
849             self.indent()
850             self.blockvisit(node.else_, if_frame)
851             self.outdent()
852
853     def visit_Macro(self, node, frame):
854         macro_frame = self.function_scoping(node, frame)
855         args = macro_frame.arguments
856         self.writeline('def macro(%s):' % ', '.join(args), node)
857         macro_frame.buffer = buf = self.temporary_identifier()
858         self.indent()
859         self.pull_locals(macro_frame)
860         self.writeline('%s = []' % buf)
861         self.blockvisit(node.body, macro_frame)
862         if self.environment.autoescape:
863             self.writeline('return Markup(concat(%s))' % buf)
864         else:
865             self.writeline("return concat(%s)" % buf)
866         self.outdent()
867         self.newline()
868         if frame.toplevel:
869             if not node.name.startswith('__'):
870                 self.write('context.exported_vars.add(%r)' % node.name)
871             self.writeline('context.vars[%r] = ' % node.name)
872         arg_tuple = ', '.join(repr(x.name) for x in node.args)
873         if len(node.args) == 1:
874             arg_tuple += ','
875         self.write('l_%s = Macro(environment, macro, %r, (%s), (' %
876                    (node.name, node.name, arg_tuple))
877         for arg in node.defaults:
878             self.visit(arg, macro_frame)
879             self.write(', ')
880         self.write('), %s, %s, %s)' % (
881             macro_frame.accesses_kwargs and '1' or '0',
882             macro_frame.accesses_varargs and '1' or '0',
883             macro_frame.accesses_caller and '1' or '0'
884         ))
885
886     def visit_CallBlock(self, node, frame):
887         call_frame = self.function_scoping(node, frame, node.iter_child_nodes
888                                            (exclude=('call',)))
889         args = call_frame.arguments
890         self.writeline('def call(%s):' % ', '.join(args), node)
891         call_frame.buffer = buf = self.temporary_identifier()
892         self.indent()
893         self.pull_locals(call_frame)
894         self.writeline('%s = []' % buf)
895         self.blockvisit(node.body, call_frame)
896         if self.environment.autoescape:
897             self.writeline("return Markup(concat(%s))" % buf)
898         else:
899             self.writeline('return concat(%s)' % buf)
900         self.outdent()
901         arg_tuple = ', '.join(repr(x.name) for x in node.args)
902         if len(node.args) == 1:
903             arg_tuple += ','
904         self.writeline('caller = Macro(environment, call, None, (%s), (' %
905                        arg_tuple)
906         for arg in node.defaults:
907             self.visit(arg, call_frame)
908             self.write(', ')
909         self.write('), %s, %s, 0)' % (
910             call_frame.accesses_kwargs and '1' or '0',
911             call_frame.accesses_varargs and '1' or '0'
912         ))
913         if frame.buffer is None:
914             self.writeline('yield ', node)
915         else:
916             self.writeline('%s.append(' % frame.buffer, node)
917         self.visit_Call(node.call, call_frame,
918                         extra_kwargs={'caller': 'caller'})
919         if frame.buffer is not None:
920             self.write(')')
921
922     def visit_FilterBlock(self, node, frame):
923         filter_frame = frame.inner()
924         filter_frame.inspect(node.iter_child_nodes())
925
926         aliases = self.collect_shadowed(filter_frame)
927         self.pull_locals(filter_frame)
928         filter_frame.buffer = buf = self.temporary_identifier()
929
930         self.writeline('%s = []' % buf, node)
931         for child in node.body:
932             self.visit(child, filter_frame)
933
934         if frame.buffer is None:
935             self.writeline('yield ', node)
936         else:
937             self.writeline('%s.append(' % frame.buffer, node)
938         self.visit_Filter(node.filter, filter_frame, 'concat(%s)' % buf)
939         if frame.buffer is not None:
940             self.write(')')
941
942     def visit_ExprStmt(self, node, frame):
943         self.newline(node)
944         self.visit(node.node, frame)
945
946     def visit_Output(self, node, frame):
947         # if we have a known extends statement, we don't output anything
948         if self.has_known_extends and frame.toplevel:
949             return
950
951         self.newline(node)
952
953         # if we are in the toplevel scope and there was already an extends
954         # statement we have to add a check that disables our yield(s) here
955         # so that they don't appear in the output.
956         outdent_later = False
957         if frame.toplevel and self.extends_so_far != 0:
958             self.writeline('if parent_template is None:')
959             self.indent()
960             outdent_later = True
961
962         # try to evaluate as many chunks as possible into a static
963         # string at compile time.
964         body = []
965         for child in node.nodes:
966             try:
967                 const = unicode(child.as_const())
968             except:
969                 body.append(child)
970                 continue
971             if body and isinstance(body[-1], list):
972                 body[-1].append(const)
973             else:
974                 body.append([const])
975
976         # if we have less than 3 nodes or less than 6 and a buffer we
977         # yield or extend
978         if len(body) < 3 or (frame.buffer is not None and len(body) < 6):
979             if frame.buffer is not None:
980                 self.writeline('%s.extend((' % frame.buffer)
981             for item in body:
982                 if isinstance(item, list):
983                     val = repr(concat(item))
984                     if frame.buffer is None:
985                         self.writeline('yield ' + val)
986                     else:
987                         self.write(val + ', ')
988                 else:
989                     if frame.buffer is None:
990                         self.writeline('yield ')
991                     close = 1
992                     if self.environment.autoescape:
993                         self.write('escape(')
994                     else:
995                         self.write('unicode(')
996                     if self.environment.finalize is not None:
997                         self.write('environment.finalize(')
998                         close += 1
999                     self.visit(item, frame)
1000                     self.write(')' * close)
1001                     if frame.buffer is not None:
1002                         self.write(', ')
1003             if frame.buffer is not None:
1004                 self.write('))')
1005
1006         # otherwise we create a format string as this is faster in that case
1007         else:
1008             format = []
1009             arguments = []
1010             for item in body:
1011                 if isinstance(item, list):
1012                     format.append(concat(item).replace('%', '%%'))
1013                 else:
1014                     format.append('%s')
1015                     arguments.append(item)
1016             if frame.buffer is None:
1017                 self.writeline('yield ')
1018             else:
1019                 self.writeline('%s.append(' % frame.buffer)
1020             self.write(repr(concat(format)) + ' % (')
1021             idx = -1
1022             self.indent()
1023             for argument in arguments:
1024                 self.newline(argument)
1025                 close = 0
1026                 if self.environment.autoescape:
1027                     self.write('escape(')
1028                     close += 1
1029                 if self.environment.finalize is not None:
1030                     self.write('environment.finalize(')
1031                     close += 1
1032                 self.visit(argument, frame)
1033                 self.write(')' * close + ',')
1034             self.outdent()
1035             self.writeline(')')
1036             if frame.buffer is not None:
1037                 self.write(')')
1038
1039         if outdent_later:
1040             self.outdent()
1041
1042     def visit_Assign(self, node, frame):
1043         self.newline(node)
1044         # toplevel assignments however go into the local namespace and
1045         # the current template's context.  We create a copy of the frame
1046         # here and add a set so that the Name visitor can add the assigned
1047         # names here.
1048         if frame.toplevel:
1049             assignment_frame = frame.copy()
1050             assignment_frame.assigned_names = set()
1051         else:
1052             assignment_frame = frame
1053         self.visit(node.target, assignment_frame)
1054         self.write(' = ')
1055         self.visit(node.node, frame)
1056
1057         # make sure toplevel assignments are added to the context.
1058         if frame.toplevel:
1059             for name in assignment_frame.assigned_names:
1060                 self.writeline('context.vars[%r] = l_%s' % (name, name))
1061                 if not name.startswith('__'):
1062                     self.writeline('context.exported_vars.add(%r)' % name)
1063
1064     def visit_Name(self, node, frame):
1065         if node.ctx == 'store' and frame.toplevel:
1066             frame.assigned_names.add(node.name)
1067         self.write('l_' + node.name)
1068
1069     def visit_MarkSafe(self, node, frame):
1070         self.write('Markup(')
1071         self.visit(node.expr, frame)
1072         self.write(')')
1073
1074     def visit_Const(self, node, frame):
1075         val = node.value
1076         if isinstance(val, float):
1077             # XXX: add checks for infinity and nan
1078             self.write(str(val))
1079         else:
1080             self.write(repr(val))
1081
1082     def visit_Tuple(self, node, frame):
1083         self.write('(')
1084         idx = -1
1085         for idx, item in enumerate(node.items):
1086             if idx:
1087                 self.write(', ')
1088             self.visit(item, frame)
1089         self.write(idx == 0 and ',)' or ')')
1090
1091     def visit_List(self, node, frame):
1092         self.write('[')
1093         for idx, item in enumerate(node.items):
1094             if idx:
1095                 self.write(', ')
1096             self.visit(item, frame)
1097         self.write(']')
1098
1099     def visit_Dict(self, node, frame):
1100         self.write('{')
1101         for idx, item in enumerate(node.items):
1102             if idx:
1103                 self.write(', ')
1104             self.visit(item.key, frame)
1105             self.write(': ')
1106             self.visit(item.value, frame)
1107         self.write('}')
1108
1109     def binop(operator):
1110         def visitor(self, node, frame):
1111             self.write('(')
1112             self.visit(node.left, frame)
1113             self.write(' %s ' % operator)
1114             self.visit(node.right, frame)
1115             self.write(')')
1116         return visitor
1117
1118     def uaop(operator):
1119         def visitor(self, node, frame):
1120             self.write('(' + operator)
1121             self.visit(node.node, frame)
1122             self.write(')')
1123         return visitor
1124
1125     visit_Add = binop('+')
1126     visit_Sub = binop('-')
1127     visit_Mul = binop('*')
1128     visit_Div = binop('/')
1129     visit_FloorDiv = binop('//')
1130     visit_Pow = binop('**')
1131     visit_Mod = binop('%')
1132     visit_And = binop('and')
1133     visit_Or = binop('or')
1134     visit_Pos = uaop('+')
1135     visit_Neg = uaop('-')
1136     visit_Not = uaop('not ')
1137     del binop, uaop
1138
1139     def visit_Concat(self, node, frame):
1140         self.write('%s((' % self.environment.autoescape and
1141                    'markup_join' or 'unicode_join')
1142         for arg in node.nodes:
1143             self.visit(arg, frame)
1144             self.write(', ')
1145         self.write('))')
1146
1147     def visit_Compare(self, node, frame):
1148         self.visit(node.expr, frame)
1149         for op in node.ops:
1150             self.visit(op, frame)
1151
1152     def visit_Operand(self, node, frame):
1153         self.write(' %s ' % operators[node.op])
1154         self.visit(node.expr, frame)
1155
1156     def visit_Subscript(self, node, frame):
1157         if isinstance(node.arg, nodes.Slice):
1158             self.visit(node.node, frame)
1159             self.write('[')
1160             self.visit(node.arg, frame)
1161             self.write(']')
1162             return
1163         try:
1164             const = node.arg.as_const()
1165             have_const = True
1166         except nodes.Impossible:
1167             have_const = False
1168         self.write('environment.subscribe(')
1169         self.visit(node.node, frame)
1170         self.write(', ')
1171         if have_const:
1172             self.write(repr(const))
1173         else:
1174             self.visit(node.arg, frame)
1175         self.write(')')
1176
1177     def visit_Slice(self, node, frame):
1178         if node.start is not None:
1179             self.visit(node.start, frame)
1180         self.write(':')
1181         if node.stop is not None:
1182             self.visit(node.stop, frame)
1183         if node.step is not None:
1184             self.write(':')
1185             self.visit(node.step, frame)
1186
1187     def visit_Filter(self, node, frame, initial=None):
1188         self.write('f_%s(' % node.name)
1189         func = self.environment.filters.get(node.name)
1190         if func is None:
1191             raise TemplateAssertionError('no filter named %r' % node.name,
1192                                          node.lineno, self.filename)
1193         if getattr(func, 'contextfilter', False):
1194             self.write('context, ')
1195         elif getattr(func, 'environmentfilter', False):
1196             self.write('environment, ')
1197         if isinstance(node.node, nodes.Filter):
1198             self.visit_Filter(node.node, frame, initial)
1199         elif node.node is None:
1200             self.write(initial)
1201         else:
1202             self.visit(node.node, frame)
1203         self.signature(node, frame)
1204         self.write(')')
1205
1206     def visit_Test(self, node, frame):
1207         self.write('t_%s(' % node.name)
1208         if node.name not in self.environment.tests:
1209             raise TemplateAssertionError('no test named %r' % node.name,
1210                                          node.lineno, self.filename)
1211         self.visit(node.node, frame)
1212         self.signature(node, frame)
1213         self.write(')')
1214
1215     def visit_CondExpr(self, node, frame):
1216         if not have_condexpr:
1217             self.write('((')
1218             self.visit(node.test, frame)
1219             self.write(') and (')
1220             self.visit(node.expr1, frame)
1221             self.write(',) or (')
1222             self.visit(node.expr2, frame)
1223             self.write(',))[0]')
1224         else:
1225             self.write('(')
1226             self.visit(node.expr1, frame)
1227             self.write(' if ')
1228             self.visit(node.test, frame)
1229             self.write(' else ')
1230             self.visit(node.expr2, frame)
1231             self.write(')')
1232
1233     def visit_Call(self, node, frame, extra_kwargs=None):
1234         if self.environment.sandboxed:
1235             self.write('environment.call(')
1236         self.visit(node.node, frame)
1237         self.write(self.environment.sandboxed and ', ' or '(')
1238         self.signature(node, frame, False, extra_kwargs)
1239         self.write(')')
1240
1241     def visit_Keyword(self, node, frame):
1242         self.write(node.key + '=')
1243         self.visit(node.value, frame)