blob: 30054e73bd0f2ab4ddec01b2f1e29dd39a495015 [file] [log] [blame]
Armin Ronachere791c2a2008-04-07 18:39:54 +02001# -*- 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"""
Armin Ronacher32a910f2008-04-26 23:21:03 +020011from time import time
Armin Ronacher8efc5222008-04-08 14:47:40 +020012from copy import copy
Armin Ronachere791c2a2008-04-07 18:39:54 +020013from random import randrange
Armin Ronacher2feed1d2008-04-26 16:26:52 +020014from keyword import iskeyword
Armin Ronachere791c2a2008-04-07 18:39:54 +020015from cStringIO import StringIO
Armin Ronacherd1ff8582008-05-11 00:30:43 +020016from itertools import chain
Armin Ronachere791c2a2008-04-07 18:39:54 +020017from jinja2 import nodes
18from jinja2.visitor import NodeVisitor, NodeTransformer
19from jinja2.exceptions import TemplateAssertionError
Armin Ronacher7ceced52008-05-03 10:15:31 +020020from jinja2.utils import Markup, concat
Armin Ronachere791c2a2008-04-07 18:39:54 +020021
22
23operators = {
24 'eq': '==',
25 'ne': '!=',
26 'gt': '>',
27 'gteq': '>=',
28 'lt': '<',
29 'lteq': '<=',
30 'in': 'in',
31 'notin': 'not in'
32}
33
Armin Ronacher3d8b7842008-04-13 13:16:50 +020034try:
35 exec '(0 if 0 else 0)'
36except SyntaxError:
37 have_condexpr = False
38else:
39 have_condexpr = True
40
41
Armin Ronacher8e8d0712008-04-16 23:10:49 +020042def generate(node, environment, name, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020043 """Generate the python source for a node tree."""
Armin Ronacher023b5e92008-05-08 11:03:10 +020044 if not isinstance(node, nodes.Template):
45 raise TypeError('Can\'t compile non template nodes')
Armin Ronacher69e12db2008-05-12 09:00:03 +020046 node.freeze()
Armin Ronacher8e8d0712008-04-16 23:10:49 +020047 generator = CodeGenerator(environment, name, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020048 generator.visit(node)
49 if stream is None:
50 return generator.stream.getvalue()
51
52
Armin Ronacher4dfc9752008-04-09 15:03:29 +020053def has_safe_repr(value):
54 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020055 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020056 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020057 if isinstance(value, (bool, int, long, float, complex, basestring,
Armin Ronacher32a910f2008-04-26 23:21:03 +020058 xrange, Markup)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020059 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020060 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020061 for item in value:
62 if not has_safe_repr(item):
63 return False
64 return True
65 elif isinstance(value, dict):
66 for key, value in value.iteritems():
67 if not has_safe_repr(key):
68 return False
69 if not has_safe_repr(value):
70 return False
71 return True
72 return False
73
74
Armin Ronacherc9705c22008-04-27 21:28:03 +020075def find_undeclared(nodes, names):
76 """Check if the names passed are accessed undeclared. The return value
77 is a set of all the undeclared names from the sequence of names found.
78 """
79 visitor = UndeclaredNameVisitor(names)
80 try:
81 for node in nodes:
82 visitor.visit(node)
83 except VisitorExit:
84 pass
85 return visitor.undeclared
86
87
Armin Ronachere791c2a2008-04-07 18:39:54 +020088class Identifiers(object):
89 """Tracks the status of identifiers in frames."""
90
91 def __init__(self):
92 # variables that are known to be declared (probably from outer
93 # frames or because they are special for the frame)
94 self.declared = set()
95
Armin Ronacher10f3ba22008-04-18 11:30:37 +020096 # undeclared variables from outer scopes
97 self.outer_undeclared = set()
98
Armin Ronachere791c2a2008-04-07 18:39:54 +020099 # names that are accessed without being explicitly declared by
100 # this one or any of the outer scopes. Names can appear both in
101 # declared and undeclared.
102 self.undeclared = set()
103
104 # names that are declared locally
105 self.declared_locally = set()
106
107 # names that are declared by parameters
108 self.declared_parameter = set()
109
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200110 # static subscribes
111 self.static_subscribes = {}
112
Armin Ronachere791c2a2008-04-07 18:39:54 +0200113 def add_special(self, name):
114 """Register a special name like `loop`."""
115 self.undeclared.discard(name)
116 self.declared.add(name)
117
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200118 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200119 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200120 if name in self.declared_locally or name in self.declared_parameter:
121 return True
122 if local_only:
123 return False
124 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +0200125
126 def find_shadowed(self):
127 """Find all the shadowed names."""
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200128 return (self.declared | self.outer_undeclared) & \
129 (self.declared_locally | self.declared_parameter)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200130
131
132class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200133 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200134
135 def __init__(self, parent=None):
136 self.identifiers = Identifiers()
Armin Ronacherfed44b52008-04-13 19:42:53 +0200137
Armin Ronacher75cfb862008-04-11 13:47:22 +0200138 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200139 self.toplevel = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200140
Armin Ronacher75cfb862008-04-11 13:47:22 +0200141 # the root frame is basically just the outermost frame, so no if
142 # conditions. This information is used to optimize inheritance
143 # situations.
144 self.rootlevel = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200145
146 # inside some tags we are using a buffer rather than yield statements.
147 # this for example affects {% filter %} or {% macro %}. If a frame
148 # is buffered this variable points to the name of the list used as
149 # buffer.
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200150 self.buffer = None
Armin Ronacherfed44b52008-04-13 19:42:53 +0200151
Armin Ronacherfed44b52008-04-13 19:42:53 +0200152 # the name of the block we're in, otherwise None.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200153 self.block = parent and parent.block or None
Armin Ronacherfed44b52008-04-13 19:42:53 +0200154
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200155 # node overlays. see `CodeGenerator.overlay` for more details
156 self.overlays = {}
157
Armin Ronacherfed44b52008-04-13 19:42:53 +0200158 # the parent of this frame
159 self.parent = parent
160
Armin Ronachere791c2a2008-04-07 18:39:54 +0200161 if parent is not None:
162 self.identifiers.declared.update(
163 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200164 parent.identifiers.declared_locally |
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200165 parent.identifiers.declared_parameter |
166 parent.identifiers.undeclared
Armin Ronachere791c2a2008-04-07 18:39:54 +0200167 )
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200168 self.identifiers.outer_undeclared.update(
169 parent.identifiers.undeclared -
170 self.identifiers.declared
171 )
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200172 self.buffer = parent.buffer
Armin Ronachere791c2a2008-04-07 18:39:54 +0200173
Armin Ronacher8efc5222008-04-08 14:47:40 +0200174 def copy(self):
175 """Create a copy of the current one."""
176 rv = copy(self)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200177 rv.identifiers = copy(self.identifiers)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200178 return rv
179
Armin Ronacherc9705c22008-04-27 21:28:03 +0200180 def inspect(self, nodes, hard_scope=False):
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200181 """Walk the node and check for identifiers. If the scope is hard (eg:
182 enforce on a python level) overrides from outer scopes are tracked
183 differently.
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200184 """
185 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200186 for node in nodes:
Armin Ronacherc9705c22008-04-27 21:28:03 +0200187 visitor.visit(node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200188
189 def inner(self):
190 """Return an inner frame."""
191 return Frame(self)
192
Armin Ronacher75cfb862008-04-11 13:47:22 +0200193 def soft(self):
194 """Return a soft frame. A soft frame may not be modified as
195 standalone thing as it shares the resources with the frame it
196 was created of, but it's not a rootlevel frame any longer.
197 """
198 rv = copy(self)
199 rv.rootlevel = False
200 return rv
201
Armin Ronachere791c2a2008-04-07 18:39:54 +0200202
Armin Ronacherc9705c22008-04-27 21:28:03 +0200203class VisitorExit(RuntimeError):
204 """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
205
206
207class DependencyFinderVisitor(NodeVisitor):
208 """A visitor that collects filter and test calls."""
209
210 def __init__(self):
211 self.filters = set()
212 self.tests = set()
213
214 def visit_Filter(self, node):
215 self.generic_visit(node)
216 self.filters.add(node.name)
217
218 def visit_Test(self, node):
219 self.generic_visit(node)
220 self.tests.add(node.name)
221
222 def visit_Block(self, node):
223 """Stop visiting at blocks."""
224
225
226class UndeclaredNameVisitor(NodeVisitor):
227 """A visitor that checks if a name is accessed without being
228 declared. This is different from the frame visitor as it will
229 not stop at closure frames.
230 """
231
232 def __init__(self, names):
233 self.names = set(names)
234 self.undeclared = set()
235
236 def visit_Name(self, node):
237 if node.ctx == 'load' and node.name in self.names:
238 self.undeclared.add(node.name)
239 if self.undeclared == self.names:
240 raise VisitorExit()
241 else:
242 self.names.discard(node.name)
243
244 def visit_Block(self, node):
245 """Stop visiting a blocks."""
246
247
Armin Ronachere791c2a2008-04-07 18:39:54 +0200248class FrameIdentifierVisitor(NodeVisitor):
249 """A visitor for `Frame.inspect`."""
250
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200251 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200252 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200253 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200254
Armin Ronacherc9705c22008-04-27 21:28:03 +0200255 def visit_Name(self, node):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200256 """All assignments to names go through this function."""
Armin Ronachere9411b42008-05-15 16:22:07 +0200257 if node.ctx == 'store':
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200258 self.identifiers.declared_locally.add(node.name)
Armin Ronachere9411b42008-05-15 16:22:07 +0200259 elif node.ctx == 'param':
260 self.identifiers.declared_parameter.add(node.name)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200261 elif node.ctx == 'load' and not \
262 self.identifiers.is_declared(node.name, self.hard_scope):
263 self.identifiers.undeclared.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200264
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200265 def visit_Subscript(self, node):
266 """Under some circumstances subscripts are aliased with local names:
267
268 - the subscript node is either already aliased or a name that was not
269 reassigned.
270 - and the subscription argument is a constant string.
271 """
272 self.generic_visit(node)
273 if isinstance(node.arg, nodes.Const) and \
274 isinstance(node.arg.value, basestring) and \
Armin Ronacher151418d2008-05-15 15:00:45 +0200275 ((isinstance(node.node, nodes.Name) and
Armin Ronachere9411b42008-05-15 16:22:07 +0200276 node.node.name not in (self.identifiers.declared_locally)) or
Armin Ronacher151418d2008-05-15 15:00:45 +0200277 node.node in self.identifiers.static_subscribes):
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200278 if node in self.identifiers.static_subscribes:
279 self.identifiers.static_subscribes[node] += 1
280 else:
281 self.identifiers.static_subscribes[node] = 1
282
Armin Ronacherc9705c22008-04-27 21:28:03 +0200283 def visit_Macro(self, node):
284 self.generic_visit(node)
285 self.identifiers.declared_locally.add(node.name)
Armin Ronacher0611e492008-04-25 23:44:14 +0200286
Armin Ronacherc9705c22008-04-27 21:28:03 +0200287 def visit_Import(self, node):
288 self.generic_visit(node)
289 self.identifiers.declared_locally.add(node.target)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200290
Armin Ronacherc9705c22008-04-27 21:28:03 +0200291 def visit_FromImport(self, node):
292 self.generic_visit(node)
293 for name in node.names:
294 if isinstance(name, tuple):
295 self.identifiers.declared_locally.add(name[1])
296 else:
297 self.identifiers.declared_locally.add(name)
298
299 def visit_Assign(self, node):
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200300 """Visit assignments in the correct order."""
Armin Ronacherc9705c22008-04-27 21:28:03 +0200301 self.visit(node.node)
302 self.visit(node.target)
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200303
Armin Ronacherc9705c22008-04-27 21:28:03 +0200304 def visit_For(self, node):
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200305 """Visiting stops at for blocks. However the block sequence
306 is visited as part of the outer scope.
307 """
Armin Ronacherc9705c22008-04-27 21:28:03 +0200308 self.visit(node.iter)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200309
Armin Ronacherc9705c22008-04-27 21:28:03 +0200310 def visit_CallBlock(self, node):
311 for child in node.iter_child_nodes(exclude=('body',)):
312 self.visit(child)
313
314 def visit_FilterBlock(self, node):
315 self.visit(node.filter)
316
317 def visit_Block(self, node):
318 """Stop visiting at blocks."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200319
320
Armin Ronacher75cfb862008-04-11 13:47:22 +0200321class CompilerExit(Exception):
322 """Raised if the compiler encountered a situation where it just
323 doesn't make sense to further process the code. Any block that
Armin Ronacher0611e492008-04-25 23:44:14 +0200324 raises such an exception is not further processed.
325 """
Armin Ronacher75cfb862008-04-11 13:47:22 +0200326
327
Armin Ronachere791c2a2008-04-07 18:39:54 +0200328class CodeGenerator(NodeVisitor):
329
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200330 def __init__(self, environment, name, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200331 if stream is None:
332 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200333 self.environment = environment
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200334 self.name = name
Armin Ronachere791c2a2008-04-07 18:39:54 +0200335 self.filename = filename
336 self.stream = stream
Armin Ronacherfed44b52008-04-13 19:42:53 +0200337
Armin Ronacher023b5e92008-05-08 11:03:10 +0200338 # aliases for imports
339 self.import_aliases = {}
340
Armin Ronacherfed44b52008-04-13 19:42:53 +0200341 # a registry for all blocks. Because blocks are moved out
342 # into the global python scope they are registered here
Armin Ronachere791c2a2008-04-07 18:39:54 +0200343 self.blocks = {}
Armin Ronacherfed44b52008-04-13 19:42:53 +0200344
345 # the number of extends statements so far
Armin Ronacher7fb38972008-04-11 13:54:28 +0200346 self.extends_so_far = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200347
348 # some templates have a rootlevel extends. In this case we
349 # can safely assume that we're a child template and do some
350 # more optimizations.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200351 self.has_known_extends = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200352
Armin Ronacherba3757b2008-04-16 19:43:16 +0200353 # the current line number
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200354 self.code_lineno = 1
Armin Ronacherba3757b2008-04-16 19:43:16 +0200355
Armin Ronacherb9e78752008-05-10 23:36:28 +0200356 # registry of all filters and tests (global, not block local)
357 self.tests = {}
358 self.filters = {}
359
Armin Ronacherba3757b2008-04-16 19:43:16 +0200360 # the debug information
361 self.debug_info = []
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200362 self._write_debug_info = None
Armin Ronacherba3757b2008-04-16 19:43:16 +0200363
Armin Ronacherfed44b52008-04-13 19:42:53 +0200364 # the number of new lines before the next write()
365 self._new_lines = 0
366
367 # the line number of the last written statement
Armin Ronachere791c2a2008-04-07 18:39:54 +0200368 self._last_line = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200369
370 # true if nothing was written so far.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200371 self._first_write = True
372
Armin Ronacherfed44b52008-04-13 19:42:53 +0200373 # used by the `temporary_identifier` method to get new
374 # unique, temporary identifier
375 self._last_identifier = 0
376
377 # the current indentation
378 self._indentation = 0
379
Armin Ronachere791c2a2008-04-07 18:39:54 +0200380 def temporary_identifier(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200381 """Get a new unique identifier."""
382 self._last_identifier += 1
383 return 't%d' % self._last_identifier
Armin Ronachere791c2a2008-04-07 18:39:54 +0200384
385 def indent(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200386 """Indent by one."""
387 self._indentation += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +0200388
Armin Ronacher8efc5222008-04-08 14:47:40 +0200389 def outdent(self, step=1):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200390 """Outdent by step."""
391 self._indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200392
Armin Ronacherc9705c22008-04-27 21:28:03 +0200393 def blockvisit(self, nodes, frame, force_generator=True):
394 """Visit a list of nodes as block in a frame. If the current frame
395 is no buffer a dummy ``if 0: yield None`` is written automatically
396 unless the force_generator parameter is set to False.
Armin Ronacherfed44b52008-04-13 19:42:53 +0200397 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200398 if frame.buffer is None and force_generator:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200399 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200400 try:
401 for node in nodes:
402 self.visit(node, frame)
403 except CompilerExit:
404 pass
Armin Ronachere791c2a2008-04-07 18:39:54 +0200405
406 def write(self, x):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200407 """Write a string into the output stream."""
408 if self._new_lines:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200409 if not self._first_write:
Armin Ronacherfed44b52008-04-13 19:42:53 +0200410 self.stream.write('\n' * self._new_lines)
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200411 self.code_lineno += self._new_lines
412 if self._write_debug_info is not None:
413 self.debug_info.append((self._write_debug_info,
414 self.code_lineno))
415 self._write_debug_info = None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200416 self._first_write = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200417 self.stream.write(' ' * self._indentation)
418 self._new_lines = 0
Armin Ronachere791c2a2008-04-07 18:39:54 +0200419 self.stream.write(x)
420
421 def writeline(self, x, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200422 """Combination of newline and write."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200423 self.newline(node, extra)
424 self.write(x)
425
426 def newline(self, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200427 """Add one or more newlines before the next write."""
428 self._new_lines = max(self._new_lines, 1 + extra)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200429 if node is not None and node.lineno != self._last_line:
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200430 self._write_debug_info = node.lineno
431 self._last_line = node.lineno
Armin Ronachere791c2a2008-04-07 18:39:54 +0200432
Armin Ronacher71082072008-04-12 14:19:36 +0200433 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200434 """Writes a function call to the stream for the current node.
435 Per default it will write a leading comma but this can be
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200436 disabled by setting have_comma to False. The extra keyword
437 arguments may not include python keywords otherwise a syntax
438 error could occour. The extra keyword arguments should be given
439 as python dict.
Armin Ronacherfed44b52008-04-13 19:42:53 +0200440 """
Armin Ronacher8efc5222008-04-08 14:47:40 +0200441 have_comma = have_comma and [True] or []
442 def touch_comma():
443 if have_comma:
444 self.write(', ')
445 else:
446 have_comma.append(True)
447
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200448 # if any of the given keyword arguments is a python keyword
449 # we have to make sure that no invalid call is created.
450 kwarg_workaround = False
451 for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
452 if iskeyword(kwarg):
453 kwarg_workaround = True
454 break
455
Armin Ronacher8efc5222008-04-08 14:47:40 +0200456 for arg in node.args:
457 touch_comma()
458 self.visit(arg, frame)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200459
460 if not kwarg_workaround:
461 for kwarg in node.kwargs:
462 touch_comma()
463 self.visit(kwarg, frame)
464 if extra_kwargs is not None:
465 for key, value in extra_kwargs.iteritems():
466 touch_comma()
467 self.write('%s=%s' % (key, value))
Armin Ronacher8efc5222008-04-08 14:47:40 +0200468 if node.dyn_args:
469 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200470 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200471 self.visit(node.dyn_args, frame)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200472
473 if kwarg_workaround:
474 touch_comma()
475 if node.dyn_kwargs is not None:
476 self.write('**dict({')
477 else:
478 self.write('**{')
479 for kwarg in node.kwargs:
480 self.write('%r: ' % kwarg.key)
481 self.visit(kwarg.value, frame)
482 self.write(', ')
483 if extra_kwargs is not None:
484 for key, value in extra_kwargs.iteritems():
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200485 self.write('%r: %s, ' % (key, value))
486 if node.dyn_kwargs is not None:
487 self.write('}, **')
488 self.visit(node.dyn_kwargs, frame)
489 self.write(')')
490 else:
491 self.write('}')
492
493 elif node.dyn_kwargs is not None:
Armin Ronacher8efc5222008-04-08 14:47:40 +0200494 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200495 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200496 self.visit(node.dyn_kwargs, frame)
497
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200498 def overlay(self, node, frame):
499 """Visit an overlay and return `True` or `False` if the overlay
500 does not exist or is exhausted. An overlay is used to replace a
501 node with another one (or an identifier) N times. This is for
502 example used to replace static subscribes before reassignments
503 of a name that occour more than one time. If a node has overlays
504 it's important that this method is called, otherwise the count
505 will be out of sync and the generated code is broken.
506 """
507 if node not in frame.overlays:
508 return False
509 overlay, count = frame.overlays[node]
510 if count is not None and count <= 0:
511 return False
512 if isinstance(overlay, basestring):
513 self.write(overlay)
514 else:
515 self.visit(overlay, frame)
516 frame.overlays[node] = (overlay, count - 1)
517 return True
518
Armin Ronacherc9705c22008-04-27 21:28:03 +0200519 def pull_locals(self, frame):
520 """Pull all the references identifiers into the local scope."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200521 for name in frame.identifiers.undeclared:
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200522 self.writeline('l_%s = context.resolve(%r)' % (name, name))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200523
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +0200524 # find all the static subscribes with a count > 2 and
525 # order them properly so that subscribes depending on other
526 # subscribes are handled later.
527 static_subscribes = [(node, count) for node, count in
528 frame.identifiers.static_subscribes.items() if
529 count >= 2]
530 if static_subscribes:
531 static_subscribes.sort(key=lambda x: type(x[0].node)
532 is nodes.Subscript)
533 for node, count in static_subscribes:
534 ident = self.temporary_identifier()
535 self.writeline(ident + ' = ')
536 self.visit(node, frame)
537 frame.overlays[node] = (ident, count)
538
Armin Ronacherc9705c22008-04-27 21:28:03 +0200539 def pull_dependencies(self, nodes):
540 """Pull all the dependencies."""
541 visitor = DependencyFinderVisitor()
542 for node in nodes:
543 visitor.visit(node)
Armin Ronacherb9e78752008-05-10 23:36:28 +0200544 for dependency in 'filters', 'tests':
545 mapping = getattr(self, dependency)
546 for name in getattr(visitor, dependency):
547 if name not in mapping:
548 mapping[name] = self.temporary_identifier()
549 self.writeline('%s = environment.%s[%r]' %
550 (mapping[name], dependency, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200551
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200552 def collect_shadowed(self, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200553 """This function returns all the shadowed variables in a dict
554 in the form name: alias and will write the required assignments
555 into the current scope. No indentation takes place.
556 """
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200557 # make sure we "backup" overridden, local identifiers
558 # TODO: we should probably optimize this and check if the
559 # identifier is in use afterwards.
560 aliases = {}
561 for name in frame.identifiers.find_shadowed():
562 aliases[name] = ident = self.temporary_identifier()
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200563 self.writeline('%s = l_%s' % (ident, name))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200564 return aliases
565
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200566 def function_scoping(self, node, frame, children=None,
567 find_special=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200568 """In Jinja a few statements require the help of anonymous
569 functions. Those are currently macros and call blocks and in
570 the future also recursive loops. As there is currently
571 technical limitation that doesn't allow reading and writing a
572 variable in a scope where the initial value is coming from an
573 outer scope, this function tries to fall back with a common
574 error message. Additionally the frame passed is modified so
575 that the argumetns are collected and callers are looked up.
576
577 This will return the modified frame.
578 """
Armin Ronacherc9705c22008-04-27 21:28:03 +0200579 # we have to iterate twice over it, make sure that works
580 if children is None:
581 children = node.iter_child_nodes()
582 children = list(children)
Armin Ronacher71082072008-04-12 14:19:36 +0200583 func_frame = frame.inner()
Armin Ronacherc9705c22008-04-27 21:28:03 +0200584 func_frame.inspect(children, hard_scope=True)
Armin Ronacher71082072008-04-12 14:19:36 +0200585
586 # variables that are undeclared (accessed before declaration) and
587 # declared locally *and* part of an outside scope raise a template
588 # assertion error. Reason: we can't generate reasonable code from
589 # it without aliasing all the variables. XXX: alias them ^^
590 overriden_closure_vars = (
591 func_frame.identifiers.undeclared &
592 func_frame.identifiers.declared &
593 (func_frame.identifiers.declared_locally |
594 func_frame.identifiers.declared_parameter)
595 )
596 if overriden_closure_vars:
597 vars = ', '.join(sorted(overriden_closure_vars))
598 raise TemplateAssertionError('It\'s not possible to set and '
599 'access variables derived from '
600 'an outer scope! (affects: %s' %
Armin Ronacher66a93442008-05-11 23:42:19 +0200601 vars, node.lineno, self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200602
603 # remove variables from a closure from the frame's undeclared
604 # identifiers.
605 func_frame.identifiers.undeclared -= (
606 func_frame.identifiers.undeclared &
607 func_frame.identifiers.declared
608 )
609
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200610 # no special variables for this scope, abort early
611 if not find_special:
612 return func_frame
613
Armin Ronacher963f97d2008-04-25 11:44:59 +0200614 func_frame.accesses_kwargs = False
615 func_frame.accesses_varargs = False
Armin Ronacher71082072008-04-12 14:19:36 +0200616 func_frame.accesses_caller = False
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200617 func_frame.arguments = args = ['l_' + x.name for x in node.args]
Armin Ronacher71082072008-04-12 14:19:36 +0200618
Armin Ronacherc9705c22008-04-27 21:28:03 +0200619 undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
620
621 if 'caller' in undeclared:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200622 func_frame.accesses_caller = True
623 func_frame.identifiers.add_special('caller')
624 args.append('l_caller')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200625 if 'kwargs' in undeclared:
Armin Ronacher963f97d2008-04-25 11:44:59 +0200626 func_frame.accesses_kwargs = True
627 func_frame.identifiers.add_special('kwargs')
628 args.append('l_kwargs')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200629 if 'varargs' in undeclared:
Armin Ronacher963f97d2008-04-25 11:44:59 +0200630 func_frame.accesses_varargs = True
631 func_frame.identifiers.add_special('varargs')
632 args.append('l_varargs')
Armin Ronacher71082072008-04-12 14:19:36 +0200633 return func_frame
634
Armin Ronachere791c2a2008-04-07 18:39:54 +0200635 # -- Visitors
636
637 def visit_Template(self, node, frame=None):
638 assert frame is None, 'no root frame allowed'
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200639 from jinja2.runtime import __all__ as exported
Armin Ronacher709f6e52008-04-28 18:18:16 +0200640 self.writeline('from __future__ import division')
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200641 self.writeline('from jinja2.runtime import ' + ', '.join(exported))
Armin Ronacher8edbe492008-04-10 20:43:43 +0200642
Armin Ronacher75cfb862008-04-11 13:47:22 +0200643 # do we have an extends tag at all? If not, we can save some
644 # overhead by just not processing any inheritance code.
645 have_extends = node.find(nodes.Extends) is not None
646
Armin Ronacher8edbe492008-04-10 20:43:43 +0200647 # find all blocks
648 for block in node.find_all(nodes.Block):
649 if block.name in self.blocks:
650 raise TemplateAssertionError('block %r defined twice' %
651 block.name, block.lineno,
Armin Ronacher66a93442008-05-11 23:42:19 +0200652 self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200653 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200654
Armin Ronacher023b5e92008-05-08 11:03:10 +0200655 # find all imports and import them
656 for import_ in node.find_all(nodes.ImportedName):
657 if import_.importname not in self.import_aliases:
658 imp = import_.importname
659 self.import_aliases[imp] = alias = self.temporary_identifier()
660 if '.' in imp:
661 module, obj = imp.rsplit('.', 1)
662 self.writeline('from %s import %s as %s' %
663 (module, obj, alias))
664 else:
665 self.writeline('import %s as %s' % (imp, alias))
666
667 # add the load name
Armin Ronacher66a93442008-05-11 23:42:19 +0200668 self.writeline('name = %r' % self.filename)
Armin Ronacher023b5e92008-05-08 11:03:10 +0200669
Armin Ronacher8efc5222008-04-08 14:47:40 +0200670 # generate the root render function.
Armin Ronacher32a910f2008-04-26 23:21:03 +0200671 self.writeline('def root(context, environment=environment):', extra=1)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200672
673 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200674 frame = Frame()
Armin Ronacherc9705c22008-04-27 21:28:03 +0200675 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200676 frame.toplevel = frame.rootlevel = True
Armin Ronacherf059ec12008-04-11 22:21:00 +0200677 self.indent()
Armin Ronacherc9705c22008-04-27 21:28:03 +0200678 if have_extends:
679 self.writeline('parent_template = None')
680 self.pull_locals(frame)
681 self.pull_dependencies(node.body)
682 if 'self' in find_undeclared(node.body, ('self',)):
683 frame.identifiers.add_special('self')
684 self.writeline('l_self = TemplateReference(context)')
685 self.blockvisit(node.body, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200686 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200687
Armin Ronacher8efc5222008-04-08 14:47:40 +0200688 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200689 if have_extends:
690 if not self.has_known_extends:
691 self.indent()
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200692 self.writeline('if parent_template is not None:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200693 self.indent()
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200694 self.writeline('for event in parent_template.'
695 'root_render_func(context):')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200696 self.indent()
697 self.writeline('yield event')
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200698 self.outdent(2 + (not self.has_known_extends))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200699
700 # at this point we now have the blocks collected and can visit them too.
701 for name, block in self.blocks.iteritems():
702 block_frame = Frame()
Armin Ronacherc9705c22008-04-27 21:28:03 +0200703 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200704 block_frame.block = name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200705 self.writeline('def block_%s(context, environment=environment):'
706 % name, block, 1)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200707 self.indent()
708 undeclared = find_undeclared(block.body, ('self', 'super'))
709 if 'self' in undeclared:
710 block_frame.identifiers.add_special('self')
711 self.writeline('l_self = TemplateReference(context)')
712 if 'super' in undeclared:
713 block_frame.identifiers.add_special('super')
714 self.writeline('l_super = context.super(%r, '
715 'block_%s)' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200716 self.pull_locals(block_frame)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200717 self.pull_dependencies(block.body)
Armin Ronacher625215e2008-04-13 16:31:08 +0200718 self.blockvisit(block.body, block_frame)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200719 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200720
Armin Ronacher75cfb862008-04-11 13:47:22 +0200721 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200722 for x in self.blocks),
723 extra=1)
724
725 # add a function that returns the debug info
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200726 self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
727 in self.debug_info))
Armin Ronacher75cfb862008-04-11 13:47:22 +0200728
Armin Ronachere791c2a2008-04-07 18:39:54 +0200729 def visit_Block(self, node, frame):
730 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200731 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200732 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200733 # if we know that we are a child template, there is no need to
734 # check if we are one
735 if self.has_known_extends:
736 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200737 if self.extends_so_far > 0:
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200738 self.writeline('if parent_template is None:')
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200739 self.indent()
740 level += 1
Armin Ronacher83fbc0f2008-05-15 12:22:28 +0200741 self.writeline('for event in context.blocks[%r][0](context):' %
Armin Ronacherc9705c22008-04-27 21:28:03 +0200742 node.name, node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200743 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200744 if frame.buffer is None:
745 self.writeline('yield event')
746 else:
747 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200748 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200749
750 def visit_Extends(self, node, frame):
751 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200752 if not frame.toplevel:
753 raise TemplateAssertionError('cannot use extend from a non '
754 'top-level scope', node.lineno,
Armin Ronacher66a93442008-05-11 23:42:19 +0200755 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200756
Armin Ronacher7fb38972008-04-11 13:54:28 +0200757 # if the number of extends statements in general is zero so
758 # far, we don't have to add a check if something extended
759 # the template before this one.
760 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200761
Armin Ronacher7fb38972008-04-11 13:54:28 +0200762 # if we have a known extends we just add a template runtime
763 # error into the generated code. We could catch that at compile
764 # time too, but i welcome it not to confuse users by throwing the
765 # same error at different times just "because we can".
766 if not self.has_known_extends:
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200767 self.writeline('if parent_template is not None:')
Armin Ronacher7fb38972008-04-11 13:54:28 +0200768 self.indent()
769 self.writeline('raise TemplateRuntimeError(%r)' %
770 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200771
Armin Ronacher7fb38972008-04-11 13:54:28 +0200772 # if we have a known extends already we don't need that code here
773 # as we know that the template execution will end here.
774 if self.has_known_extends:
775 raise CompilerExit()
776 self.outdent()
777
Armin Ronacher9d42abf2008-05-14 18:10:41 +0200778 self.writeline('parent_template = environment.get_template(', node)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200779 self.visit(node.template, frame)
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200780 self.write(', %r)' % self.name)
781 self.writeline('for name, parent_block in parent_template.'
782 'blocks.iteritems():')
783 self.indent()
784 self.writeline('context.blocks.setdefault(name, []).'
Armin Ronacher83fbc0f2008-05-15 12:22:28 +0200785 'append(parent_block)')
Armin Ronacher203bfcb2008-04-24 21:54:44 +0200786 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200787
788 # if this extends statement was in the root level we can take
789 # advantage of that information and simplify the generated code
790 # in the top level from this point onwards
Armin Ronacher27069d72008-05-11 19:48:12 +0200791 if frame.rootlevel:
792 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200793
Armin Ronacher7fb38972008-04-11 13:54:28 +0200794 # and now we have one more
795 self.extends_so_far += 1
796
Armin Ronacherf059ec12008-04-11 22:21:00 +0200797 def visit_Include(self, node, frame):
798 """Handles includes."""
Armin Ronacherea847c52008-05-02 20:04:32 +0200799 if node.with_context:
800 self.writeline('template = environment.get_template(', node)
801 self.visit(node.template, frame)
802 self.write(', %r)' % self.name)
803 self.writeline('for event in template.root_render_func('
804 'template.new_context(context.parent, True)):')
805 else:
806 self.writeline('for event in environment.get_template(', node)
807 self.visit(node.template, frame)
808 self.write(', %r).module._TemplateModule__body_stream:' %
809 self.name)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200810 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200811 if frame.buffer is None:
812 self.writeline('yield event')
813 else:
814 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200815 self.outdent()
816
Armin Ronacher0611e492008-04-25 23:44:14 +0200817 def visit_Import(self, node, frame):
818 """Visit regular imports."""
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200819 self.writeline('l_%s = ' % node.target, node)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200820 if frame.toplevel:
Armin Ronacher53042292008-04-26 18:30:19 +0200821 self.write('context.vars[%r] = ' % node.target)
Armin Ronacher0611e492008-04-25 23:44:14 +0200822 self.write('environment.get_template(')
823 self.visit(node.template, frame)
Armin Ronacherea847c52008-05-02 20:04:32 +0200824 self.write(', %r).' % self.name)
825 if node.with_context:
826 self.write('make_module(context.parent, True)')
827 else:
828 self.write('module')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200829 if frame.toplevel and not node.target.startswith('__'):
Armin Ronacher53042292008-04-26 18:30:19 +0200830 self.writeline('context.exported_vars.discard(%r)' % node.target)
Armin Ronacher0611e492008-04-25 23:44:14 +0200831
832 def visit_FromImport(self, node, frame):
833 """Visit named imports."""
834 self.newline(node)
835 self.write('included_template = environment.get_template(')
836 self.visit(node.template, frame)
Armin Ronacherea847c52008-05-02 20:04:32 +0200837 self.write(', %r).' % self.name)
838 if node.with_context:
839 self.write('make_module(context.parent, True)')
840 else:
841 self.write('module')
Armin Ronacher0611e492008-04-25 23:44:14 +0200842 for name in node.names:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200843 if isinstance(name, tuple):
844 name, alias = name
845 else:
846 alias = name
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200847 self.writeline('l_%s = getattr(included_template, '
848 '%r, missing)' % (alias, name))
849 self.writeline('if l_%s is missing:' % alias)
Armin Ronacher0611e492008-04-25 23:44:14 +0200850 self.indent()
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200851 self.writeline('l_%s = environment.undefined(%r %% '
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200852 'included_template.name, '
Armin Ronacher0a2ac692008-05-13 01:03:08 +0200853 'name=%r)' %
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200854 (alias, 'the template %r does not export '
Armin Ronacher0a2ac692008-05-13 01:03:08 +0200855 'the requested name ' + repr(name), name))
Armin Ronacher0611e492008-04-25 23:44:14 +0200856 self.outdent()
857 if frame.toplevel:
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200858 self.writeline('context.vars[%r] = l_%s' % (alias, alias))
Armin Ronacherc9705c22008-04-27 21:28:03 +0200859 if not alias.startswith('__'):
860 self.writeline('context.exported_vars.discard(%r)' % alias)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200861
Armin Ronachere791c2a2008-04-07 18:39:54 +0200862 def visit_For(self, node, frame):
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200863 # when calculating the nodes for the inner frame we have to exclude
864 # the iterator contents from it
865 children = node.iter_child_nodes(exclude=('iter',))
866
867 if node.recursive:
868 loop_frame = self.function_scoping(node, frame, children,
869 find_special=False)
870 else:
871 loop_frame = frame.inner()
872 loop_frame.inspect(children)
873
874 extended_loop = node.recursive or node.else_ or \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200875 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200876 if extended_loop:
877 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200878
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200879 # if we don't have an recursive loop we have to find the shadowed
880 # variables at that point
881 if not node.recursive:
882 aliases = self.collect_shadowed(loop_frame)
883
884 # otherwise we set up a buffer and add a function def
885 else:
886 loop_frame.buffer = buf = self.temporary_identifier()
887 self.writeline('def loop(reciter, loop_render_func):', node)
888 self.indent()
889 self.writeline('%s = []' % buf, node)
890 aliases = {}
891
Armin Ronacherc9705c22008-04-27 21:28:03 +0200892 self.pull_locals(loop_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200893 if node.else_:
894 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200895
896 self.newline(node)
897 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200898 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200899 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200900
901 # the expression pointing to the parent loop. We make the
902 # undefined a bit more debug friendly at the same time.
903 parent_loop = 'loop' in aliases and aliases['loop'] \
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200904 or "environment.undefined(%r, name='loop')" % "'loop' " \
905 'is undefined. "the filter section of a loop as well ' \
906 'as the else block doesn\'t have access to the ' \
907 "special 'loop' variable of the current loop. " \
908 "Because there is no parent loop it's undefined."
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200909
910 # if we have an extened loop and a node test, we filter in the
911 # "outer frame".
912 if extended_loop and node.test is not None:
913 self.write('(')
914 self.visit(node.target, loop_frame)
915 self.write(' for ')
916 self.visit(node.target, loop_frame)
917 self.write(' in ')
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200918 if node.recursive:
919 self.write('reciter')
920 else:
921 self.visit(node.iter, loop_frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200922 self.write(' if (')
923 test_frame = loop_frame.copy()
Armin Ronacherc9705c22008-04-27 21:28:03 +0200924 self.writeline('l_loop = ' + parent_loop)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200925 self.visit(node.test, test_frame)
926 self.write('))')
927
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200928 elif node.recursive:
929 self.write('reciter')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200930 else:
931 self.visit(node.iter, loop_frame)
932
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200933 if node.recursive:
934 self.write(', recurse=loop_render_func):')
935 else:
936 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200937
938 # tests in not extended loops become a continue
939 if not extended_loop and node.test is not None:
940 self.indent()
Armin Ronacher47a506f2008-05-06 12:17:23 +0200941 self.writeline('if not ')
Armin Ronacher32a910f2008-04-26 23:21:03 +0200942 self.visit(node.test, loop_frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200943 self.write(':')
944 self.indent()
945 self.writeline('continue')
946 self.outdent(2)
947
Armin Ronacherc9705c22008-04-27 21:28:03 +0200948 self.indent()
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200949 self.blockvisit(node.body, loop_frame, force_generator=True)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200950 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200951
952 if node.else_:
953 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200954 self.indent()
955 self.writeline('l_loop = ' + parent_loop)
Armin Ronacher625215e2008-04-13 16:31:08 +0200956 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200957 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200958
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200959 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200960 for name, alias in aliases.iteritems():
Armin Ronacherd1ff8582008-05-11 00:30:43 +0200961 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200962
Armin Ronacher1e1e8902008-05-11 23:21:16 +0200963 # if the node was recursive we have to return the buffer contents
964 # and start the iteration code
965 if node.recursive:
966 if self.environment.autoescape:
967 self.writeline('return Markup(concat(%s))' % buf)
968 else:
969 self.writeline('return concat(%s)' % buf)
970 self.outdent()
971 if frame.buffer is None:
972 self.writeline('yield loop(', node)
973 else:
974 self.writeline('%s.append(loop(' % frame.buffer, node)
975 self.visit(node.iter, frame)
976 self.write(', loop)')
977 if frame.buffer is not None:
978 self.write(')')
979
Armin Ronachere791c2a2008-04-07 18:39:54 +0200980 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200981 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200982 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200983 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200984 self.write(':')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200985 self.indent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200986 self.blockvisit(node.body, if_frame)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200987 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200988 if node.else_:
989 self.writeline('else:')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200990 self.indent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200991 self.blockvisit(node.else_, if_frame)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200992 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200993
Armin Ronacher8efc5222008-04-08 14:47:40 +0200994 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200995 macro_frame = self.function_scoping(node, frame)
996 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200997 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200998 macro_frame.buffer = buf = self.temporary_identifier()
999 self.indent()
Armin Ronacherc9705c22008-04-27 21:28:03 +02001000 self.pull_locals(macro_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +02001001 self.writeline('%s = []' % buf)
Armin Ronacherc9705c22008-04-27 21:28:03 +02001002 self.blockvisit(node.body, macro_frame)
Armin Ronacherd1342312008-04-28 12:20:12 +02001003 if self.environment.autoescape:
1004 self.writeline('return Markup(concat(%s))' % buf)
1005 else:
1006 self.writeline("return concat(%s)" % buf)
Armin Ronacher625215e2008-04-13 16:31:08 +02001007 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +02001008 self.newline()
1009 if frame.toplevel:
Armin Ronacherc9705c22008-04-27 21:28:03 +02001010 if not node.name.startswith('__'):
1011 self.write('context.exported_vars.add(%r)' % node.name)
Armin Ronacher32a910f2008-04-26 23:21:03 +02001012 self.writeline('context.vars[%r] = ' % node.name)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001013 arg_tuple = ', '.join(repr(x.name) for x in node.args)
1014 if len(node.args) == 1:
1015 arg_tuple += ','
Armin Ronacherd1ff8582008-05-11 00:30:43 +02001016 self.write('l_%s = Macro(environment, macro, %r, (%s), (' %
1017 (node.name, node.name, arg_tuple))
Armin Ronacher4f62a9f2008-04-08 18:09:13 +02001018 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +02001019 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +02001020 self.write(', ')
Armin Ronacher963f97d2008-04-25 11:44:59 +02001021 self.write('), %s, %s, %s)' % (
1022 macro_frame.accesses_kwargs and '1' or '0',
1023 macro_frame.accesses_varargs and '1' or '0',
Armin Ronacher00d5d212008-04-13 01:10:18 +02001024 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +02001025 ))
1026
1027 def visit_CallBlock(self, node, frame):
Armin Ronacherc9705c22008-04-27 21:28:03 +02001028 call_frame = self.function_scoping(node, frame, node.iter_child_nodes
1029 (exclude=('call',)))
Armin Ronacher71082072008-04-12 14:19:36 +02001030 args = call_frame.arguments
1031 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +02001032 call_frame.buffer = buf = self.temporary_identifier()
1033 self.indent()
Armin Ronacherc9705c22008-04-27 21:28:03 +02001034 self.pull_locals(call_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +02001035 self.writeline('%s = []' % buf)
Armin Ronacherc9705c22008-04-27 21:28:03 +02001036 self.blockvisit(node.body, call_frame)
Armin Ronacherd1342312008-04-28 12:20:12 +02001037 if self.environment.autoescape:
1038 self.writeline("return Markup(concat(%s))" % buf)
1039 else:
1040 self.writeline('return concat(%s)' % buf)
Armin Ronacher625215e2008-04-13 16:31:08 +02001041 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +02001042 arg_tuple = ', '.join(repr(x.name) for x in node.args)
1043 if len(node.args) == 1:
1044 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +02001045 self.writeline('caller = Macro(environment, call, None, (%s), (' %
1046 arg_tuple)
Armin Ronacher71082072008-04-12 14:19:36 +02001047 for arg in node.defaults:
Armin Ronacherc9705c22008-04-27 21:28:03 +02001048 self.visit(arg, call_frame)
Armin Ronacher71082072008-04-12 14:19:36 +02001049 self.write(', ')
Armin Ronacher963f97d2008-04-25 11:44:59 +02001050 self.write('), %s, %s, 0)' % (
1051 call_frame.accesses_kwargs and '1' or '0',
1052 call_frame.accesses_varargs and '1' or '0'
1053 ))
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001054 if frame.buffer is None:
1055 self.writeline('yield ', node)
1056 else:
1057 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher2feed1d2008-04-26 16:26:52 +02001058 self.visit_Call(node.call, call_frame,
1059 extra_kwargs={'caller': 'caller'})
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001060 if frame.buffer is not None:
1061 self.write(')')
1062
1063 def visit_FilterBlock(self, node, frame):
1064 filter_frame = frame.inner()
1065 filter_frame.inspect(node.iter_child_nodes())
1066
1067 aliases = self.collect_shadowed(filter_frame)
Armin Ronacherc9705c22008-04-27 21:28:03 +02001068 self.pull_locals(filter_frame)
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001069 filter_frame.buffer = buf = self.temporary_identifier()
1070
1071 self.writeline('%s = []' % buf, node)
1072 for child in node.body:
1073 self.visit(child, filter_frame)
1074
1075 if frame.buffer is None:
1076 self.writeline('yield ', node)
1077 else:
1078 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacherde6bf712008-04-26 01:44:14 +02001079 self.visit_Filter(node.filter, filter_frame, 'concat(%s)' % buf)
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001080 if frame.buffer is not None:
1081 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001082
Armin Ronachere791c2a2008-04-07 18:39:54 +02001083 def visit_ExprStmt(self, node, frame):
1084 self.newline(node)
Armin Ronacher6ce170c2008-04-25 12:32:36 +02001085 self.visit(node.node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001086
1087 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +02001088 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +02001089 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +02001090 return
Armin Ronachere791c2a2008-04-07 18:39:54 +02001091
Armin Ronacher75cfb862008-04-11 13:47:22 +02001092 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +02001093
Armin Ronacher7fb38972008-04-11 13:54:28 +02001094 # if we are in the toplevel scope and there was already an extends
1095 # statement we have to add a check that disables our yield(s) here
1096 # so that they don't appear in the output.
1097 outdent_later = False
1098 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher203bfcb2008-04-24 21:54:44 +02001099 self.writeline('if parent_template is None:')
Armin Ronacher75cfb862008-04-11 13:47:22 +02001100 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +02001101 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +02001102
Armin Ronachere791c2a2008-04-07 18:39:54 +02001103 # try to evaluate as many chunks as possible into a static
1104 # string at compile time.
1105 body = []
1106 for child in node.nodes:
1107 try:
1108 const = unicode(child.as_const())
1109 except:
1110 body.append(child)
1111 continue
1112 if body and isinstance(body[-1], list):
1113 body[-1].append(const)
1114 else:
1115 body.append([const])
1116
Armin Ronacher32a910f2008-04-26 23:21:03 +02001117 # if we have less than 3 nodes or less than 6 and a buffer we
Armin Ronacher1e1e8902008-05-11 23:21:16 +02001118 # yield or extend/append
Armin Ronacher32a910f2008-04-26 23:21:03 +02001119 if len(body) < 3 or (frame.buffer is not None and len(body) < 6):
1120 if frame.buffer is not None:
Armin Ronacher1e1e8902008-05-11 23:21:16 +02001121 # for one item we append, for more we extend
1122 if len(body) == 1:
1123 self.writeline('%s.append(' % frame.buffer)
1124 else:
1125 self.writeline('%s.extend((' % frame.buffer)
Armin Ronacher1f627ff2008-05-15 13:23:26 +02001126 self.indent()
Armin Ronachere791c2a2008-04-07 18:39:54 +02001127 for item in body:
1128 if isinstance(item, list):
Armin Ronacherde6bf712008-04-26 01:44:14 +02001129 val = repr(concat(item))
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001130 if frame.buffer is None:
1131 self.writeline('yield ' + val)
1132 else:
Armin Ronacher1f627ff2008-05-15 13:23:26 +02001133 self.writeline(val + ', ')
Armin Ronachere791c2a2008-04-07 18:39:54 +02001134 else:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001135 if frame.buffer is None:
Armin Ronacher32a910f2008-04-26 23:21:03 +02001136 self.writeline('yield ')
Armin Ronacher1f627ff2008-05-15 13:23:26 +02001137 else:
1138 self.newline(item)
Armin Ronacherd1342312008-04-28 12:20:12 +02001139 close = 1
1140 if self.environment.autoescape:
1141 self.write('escape(')
1142 else:
1143 self.write('unicode(')
1144 if self.environment.finalize is not None:
1145 self.write('environment.finalize(')
1146 close += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +02001147 self.visit(item, frame)
Armin Ronacherd1342312008-04-28 12:20:12 +02001148 self.write(')' * close)
Armin Ronacher32a910f2008-04-26 23:21:03 +02001149 if frame.buffer is not None:
1150 self.write(', ')
1151 if frame.buffer is not None:
Armin Ronacher1e1e8902008-05-11 23:21:16 +02001152 # close the open parentheses
Armin Ronacher1f627ff2008-05-15 13:23:26 +02001153 self.outdent()
1154 self.writeline(len(body) == 1 and ')' or '))')
Armin Ronachere791c2a2008-04-07 18:39:54 +02001155
1156 # otherwise we create a format string as this is faster in that case
1157 else:
1158 format = []
1159 arguments = []
1160 for item in body:
1161 if isinstance(item, list):
Armin Ronacherde6bf712008-04-26 01:44:14 +02001162 format.append(concat(item).replace('%', '%%'))
Armin Ronachere791c2a2008-04-07 18:39:54 +02001163 else:
1164 format.append('%s')
1165 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001166 if frame.buffer is None:
1167 self.writeline('yield ')
1168 else:
1169 self.writeline('%s.append(' % frame.buffer)
Armin Ronacherde6bf712008-04-26 01:44:14 +02001170 self.write(repr(concat(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +02001171 idx = -1
Armin Ronacher8e8d0712008-04-16 23:10:49 +02001172 for argument in arguments:
Armin Ronacherd1342312008-04-28 12:20:12 +02001173 close = 0
1174 if self.environment.autoescape:
1175 self.write('escape(')
1176 close += 1
1177 if self.environment.finalize is not None:
1178 self.write('environment.finalize(')
1179 close += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +02001180 self.visit(argument, frame)
Armin Ronacher1f627ff2008-05-15 13:23:26 +02001181 self.write(')' * close + ', ')
1182 self.write(')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001183 if frame.buffer is not None:
1184 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +02001185
Armin Ronacher7fb38972008-04-11 13:54:28 +02001186 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +02001187 self.outdent()
1188
Armin Ronacher8efc5222008-04-08 14:47:40 +02001189 def visit_Assign(self, node, frame):
1190 self.newline(node)
1191 # toplevel assignments however go into the local namespace and
1192 # the current template's context. We create a copy of the frame
1193 # here and add a set so that the Name visitor can add the assigned
1194 # names here.
1195 if frame.toplevel:
1196 assignment_frame = frame.copy()
1197 assignment_frame.assigned_names = set()
1198 else:
1199 assignment_frame = frame
1200 self.visit(node.target, assignment_frame)
1201 self.write(' = ')
1202 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +02001203
1204 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +02001205 if frame.toplevel:
Armin Ronacher69e12db2008-05-12 09:00:03 +02001206 public_names = [x for x in assignment_frame.assigned_names
1207 if not x.startswith('__')]
1208 if len(assignment_frame.assigned_names) == 1:
1209 name = iter(assignment_frame.assigned_names).next()
Armin Ronacherd1ff8582008-05-11 00:30:43 +02001210 self.writeline('context.vars[%r] = l_%s' % (name, name))
Armin Ronacher69e12db2008-05-12 09:00:03 +02001211 else:
1212 self.writeline('context.vars.update({')
1213 for idx, name in enumerate(assignment_frame.assigned_names):
1214 if idx:
1215 self.write(', ')
1216 self.write('%r: l_%s' % (name, name))
1217 self.write('})')
1218 if public_names:
1219 if len(public_names) == 1:
1220 self.writeline('context.exported_vars.add(%r)' %
1221 public_names[0])
1222 else:
1223 self.writeline('context.exported_vars.update((%s))' %
1224 ', '.join(map(repr, public_names)))
Armin Ronacher8efc5222008-04-08 14:47:40 +02001225
Armin Ronachere791c2a2008-04-07 18:39:54 +02001226 def visit_Name(self, node, frame):
Armin Ronacherc9705c22008-04-27 21:28:03 +02001227 if node.ctx == 'store' and frame.toplevel:
1228 frame.assigned_names.add(node.name)
Armin Ronacherd1ff8582008-05-11 00:30:43 +02001229 self.write('l_' + node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001230
Armin Ronacherd84ec462008-04-29 13:43:16 +02001231 def visit_MarkSafe(self, node, frame):
1232 self.write('Markup(')
1233 self.visit(node.expr, frame)
1234 self.write(')')
1235
Armin Ronacher023b5e92008-05-08 11:03:10 +02001236 def visit_EnvironmentAttribute(self, node, frame):
1237 self.write('environment.' + node.name)
1238
1239 def visit_ExtensionAttribute(self, node, frame):
1240 self.write('environment.extensions[%r].%s' % (node.identifier, node.attr))
1241
1242 def visit_ImportedName(self, node, frame):
1243 self.write(self.import_aliases[node.importname])
1244
1245 def visit_InternalName(self, node, frame):
1246 self.write(node.name)
1247
Armin Ronachere791c2a2008-04-07 18:39:54 +02001248 def visit_Const(self, node, frame):
1249 val = node.value
1250 if isinstance(val, float):
1251 # XXX: add checks for infinity and nan
1252 self.write(str(val))
1253 else:
1254 self.write(repr(val))
1255
Armin Ronacher8efc5222008-04-08 14:47:40 +02001256 def visit_Tuple(self, node, frame):
1257 self.write('(')
1258 idx = -1
1259 for idx, item in enumerate(node.items):
1260 if idx:
1261 self.write(', ')
1262 self.visit(item, frame)
1263 self.write(idx == 0 and ',)' or ')')
1264
Armin Ronacher8edbe492008-04-10 20:43:43 +02001265 def visit_List(self, node, frame):
1266 self.write('[')
1267 for idx, item in enumerate(node.items):
1268 if idx:
1269 self.write(', ')
1270 self.visit(item, frame)
1271 self.write(']')
1272
1273 def visit_Dict(self, node, frame):
1274 self.write('{')
1275 for idx, item in enumerate(node.items):
1276 if idx:
1277 self.write(', ')
1278 self.visit(item.key, frame)
1279 self.write(': ')
1280 self.visit(item.value, frame)
1281 self.write('}')
1282
Armin Ronachere791c2a2008-04-07 18:39:54 +02001283 def binop(operator):
1284 def visitor(self, node, frame):
1285 self.write('(')
1286 self.visit(node.left, frame)
1287 self.write(' %s ' % operator)
1288 self.visit(node.right, frame)
1289 self.write(')')
1290 return visitor
1291
1292 def uaop(operator):
1293 def visitor(self, node, frame):
1294 self.write('(' + operator)
Armin Ronacher9a822052008-04-17 18:44:07 +02001295 self.visit(node.node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001296 self.write(')')
1297 return visitor
1298
1299 visit_Add = binop('+')
1300 visit_Sub = binop('-')
1301 visit_Mul = binop('*')
1302 visit_Div = binop('/')
1303 visit_FloorDiv = binop('//')
1304 visit_Pow = binop('**')
1305 visit_Mod = binop('%')
1306 visit_And = binop('and')
1307 visit_Or = binop('or')
1308 visit_Pos = uaop('+')
1309 visit_Neg = uaop('-')
1310 visit_Not = uaop('not ')
1311 del binop, uaop
1312
Armin Ronacherd1342312008-04-28 12:20:12 +02001313 def visit_Concat(self, node, frame):
Armin Ronacherfdf95302008-05-11 22:20:51 +02001314 self.write('%s((' % (self.environment.autoescape and
1315 'markup_join' or 'unicode_join'))
Armin Ronacherd1342312008-04-28 12:20:12 +02001316 for arg in node.nodes:
1317 self.visit(arg, frame)
1318 self.write(', ')
1319 self.write('))')
1320
Armin Ronachere791c2a2008-04-07 18:39:54 +02001321 def visit_Compare(self, node, frame):
1322 self.visit(node.expr, frame)
1323 for op in node.ops:
1324 self.visit(op, frame)
1325
1326 def visit_Operand(self, node, frame):
1327 self.write(' %s ' % operators[node.op])
1328 self.visit(node.expr, frame)
1329
1330 def visit_Subscript(self, node, frame):
Armin Ronacherb3a1fcf2008-05-15 11:04:14 +02001331 # subscripts can have overlays
1332 if self.overlay(node, frame):
1333 return
1334
Armin Ronacher08a6a3b2008-05-13 15:35:47 +02001335 # slices or integer subscriptions bypass the subscribe
1336 # method if we can determine that at compile time.
1337 if isinstance(node.arg, nodes.Slice) or \
1338 (isinstance(node.arg, nodes.Const) and
1339 isinstance(node.arg.value, (int, long))):
Armin Ronacher8efc5222008-04-08 14:47:40 +02001340 self.visit(node.node, frame)
1341 self.write('[')
1342 self.visit(node.arg, frame)
1343 self.write(']')
1344 return
1345 try:
1346 const = node.arg.as_const()
1347 have_const = True
1348 except nodes.Impossible:
1349 have_const = False
Armin Ronacherc63243e2008-04-14 22:53:58 +02001350 self.write('environment.subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +02001351 self.visit(node.node, frame)
1352 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001353 if have_const:
1354 self.write(repr(const))
1355 else:
1356 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +02001357 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001358
1359 def visit_Slice(self, node, frame):
1360 if node.start is not None:
1361 self.visit(node.start, frame)
1362 self.write(':')
1363 if node.stop is not None:
1364 self.visit(node.stop, frame)
1365 if node.step is not None:
1366 self.write(':')
1367 self.visit(node.step, frame)
1368
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001369 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherb9e78752008-05-10 23:36:28 +02001370 self.write(self.filters[node.name] + '(')
Christoph Hack80909862008-04-14 01:35:10 +02001371 func = self.environment.filters.get(node.name)
Armin Ronacher0611e492008-04-25 23:44:14 +02001372 if func is None:
1373 raise TemplateAssertionError('no filter named %r' % node.name,
1374 node.lineno, self.filename)
Christoph Hack80909862008-04-14 01:35:10 +02001375 if getattr(func, 'contextfilter', False):
1376 self.write('context, ')
Armin Ronacher9a027f42008-04-17 11:13:40 +02001377 elif getattr(func, 'environmentfilter', False):
1378 self.write('environment, ')
Christoph Hack80909862008-04-14 01:35:10 +02001379 if isinstance(node.node, nodes.Filter):
1380 self.visit_Filter(node.node, frame, initial)
1381 elif node.node is None:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001382 self.write(initial)
1383 else:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001384 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +02001385 self.signature(node, frame)
1386 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001387
1388 def visit_Test(self, node, frame):
Armin Ronacherb9e78752008-05-10 23:36:28 +02001389 self.write(self.tests[node.name] + '(')
Armin Ronacher0611e492008-04-25 23:44:14 +02001390 if node.name not in self.environment.tests:
1391 raise TemplateAssertionError('no test named %r' % node.name,
1392 node.lineno, self.filename)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001393 self.visit(node.node, frame)
1394 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001395 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001396
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001397 def visit_CondExpr(self, node, frame):
1398 if not have_condexpr:
1399 self.write('((')
1400 self.visit(node.test, frame)
1401 self.write(') and (')
1402 self.visit(node.expr1, frame)
1403 self.write(',) or (')
1404 self.visit(node.expr2, frame)
1405 self.write(',))[0]')
1406 else:
1407 self.write('(')
1408 self.visit(node.expr1, frame)
1409 self.write(' if ')
1410 self.visit(node.test, frame)
1411 self.write(' else ')
1412 self.visit(node.expr2, frame)
1413 self.write(')')
1414
Armin Ronacher71082072008-04-12 14:19:36 +02001415 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacherc63243e2008-04-14 22:53:58 +02001416 if self.environment.sandboxed:
1417 self.write('environment.call(')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001418 self.visit(node.node, frame)
Armin Ronacherc63243e2008-04-14 22:53:58 +02001419 self.write(self.environment.sandboxed and ', ' or '(')
Armin Ronacher71082072008-04-12 14:19:36 +02001420 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001421 self.write(')')
1422
1423 def visit_Keyword(self, node, frame):
Armin Ronacher2e9396b2008-04-16 14:21:57 +02001424 self.write(node.key + '=')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001425 self.visit(node.value, frame)