blob: e291f1d03a880393c34bb3f5703324a064b581bf [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 Ronacher8efc5222008-04-08 14:47:40 +020011from copy import copy
Armin Ronachere791c2a2008-04-07 18:39:54 +020012from random import randrange
Armin Ronachere791c2a2008-04-07 18:39:54 +020013from cStringIO import StringIO
14from jinja2 import nodes
15from jinja2.visitor import NodeVisitor, NodeTransformer
16from jinja2.exceptions import TemplateAssertionError
Armin Ronacher4dfc9752008-04-09 15:03:29 +020017from jinja2.runtime import StaticLoopContext
Armin Ronachere791c2a2008-04-07 18:39:54 +020018
19
20operators = {
21 'eq': '==',
22 'ne': '!=',
23 'gt': '>',
24 'gteq': '>=',
25 'lt': '<',
26 'lteq': '<=',
27 'in': 'in',
28 'notin': 'not in'
29}
30
31
Christoph Hack65642a52008-04-08 14:46:56 +020032def generate(node, environment, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020033 """Generate the python source for a node tree."""
Armin Ronachere791c2a2008-04-07 18:39:54 +020034 is_child = node.find(nodes.Extends) is not None
Christoph Hack65642a52008-04-08 14:46:56 +020035 generator = CodeGenerator(environment, is_child, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020036 generator.visit(node)
37 if stream is None:
38 return generator.stream.getvalue()
39
40
Armin Ronacher4dfc9752008-04-09 15:03:29 +020041def has_safe_repr(value):
42 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020043 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020044 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020045 if isinstance(value, (bool, int, long, float, complex, basestring,
46 StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020047 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020048 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020049 for item in value:
50 if not has_safe_repr(item):
51 return False
52 return True
53 elif isinstance(value, dict):
54 for key, value in value.iteritems():
55 if not has_safe_repr(key):
56 return False
57 if not has_safe_repr(value):
58 return False
59 return True
60 return False
61
62
Armin Ronachere791c2a2008-04-07 18:39:54 +020063class Identifiers(object):
64 """Tracks the status of identifiers in frames."""
65
66 def __init__(self):
67 # variables that are known to be declared (probably from outer
68 # frames or because they are special for the frame)
69 self.declared = set()
70
71 # names that are accessed without being explicitly declared by
72 # this one or any of the outer scopes. Names can appear both in
73 # declared and undeclared.
74 self.undeclared = set()
75
76 # names that are declared locally
77 self.declared_locally = set()
78
79 # names that are declared by parameters
80 self.declared_parameter = set()
81
Armin Ronacherf059ec12008-04-11 22:21:00 +020082 # filters/tests that are referenced
Armin Ronacherd4c64f72008-04-11 17:15:29 +020083 self.filters = set()
Armin Ronacherf059ec12008-04-11 22:21:00 +020084 self.tests = set()
Christoph Hack65642a52008-04-08 14:46:56 +020085
Armin Ronachere791c2a2008-04-07 18:39:54 +020086 def add_special(self, name):
87 """Register a special name like `loop`."""
88 self.undeclared.discard(name)
89 self.declared.add(name)
90
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020091 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020092 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020093 if name in self.declared_locally or name in self.declared_parameter:
94 return True
95 if local_only:
96 return False
97 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +020098
99 def find_shadowed(self):
100 """Find all the shadowed names."""
101 return self.declared & (self.declared_locally | self.declared_parameter)
102
103
104class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200105 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200106
107 def __init__(self, parent=None):
108 self.identifiers = Identifiers()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200109 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200110 self.toplevel = False
Armin Ronacher75cfb862008-04-11 13:47:22 +0200111 # the root frame is basically just the outermost frame, so no if
112 # conditions. This information is used to optimize inheritance
113 # situations.
114 self.rootlevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200115 self.parent = parent
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200116 self.buffer = None
Armin Ronacher8efc5222008-04-08 14:47:40 +0200117 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200118 if parent is not None:
119 self.identifiers.declared.update(
120 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200121 parent.identifiers.declared_locally |
122 parent.identifiers.declared_parameter
123 )
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200124 self.buffer = parent.buffer
Armin Ronachere791c2a2008-04-07 18:39:54 +0200125
Armin Ronacher8efc5222008-04-08 14:47:40 +0200126 def copy(self):
127 """Create a copy of the current one."""
128 rv = copy(self)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200129 rv.identifiers = copy(self.identifiers)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200130 return rv
131
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200132 def inspect(self, nodes, hard_scope=False):
133 """Walk the node and check for identifiers. If the scope
134 is hard (eg: enforce on a python level) overrides from outer
135 scopes are tracked differently.
136 """
137 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200138 for node in nodes:
139 visitor.visit(node)
140
141 def inner(self):
142 """Return an inner frame."""
143 return Frame(self)
144
Armin Ronacher75cfb862008-04-11 13:47:22 +0200145 def soft(self):
146 """Return a soft frame. A soft frame may not be modified as
147 standalone thing as it shares the resources with the frame it
148 was created of, but it's not a rootlevel frame any longer.
149 """
150 rv = copy(self)
151 rv.rootlevel = False
152 return rv
153
Armin Ronachere791c2a2008-04-07 18:39:54 +0200154
155class FrameIdentifierVisitor(NodeVisitor):
156 """A visitor for `Frame.inspect`."""
157
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200158 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200159 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200160 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200161
162 def visit_Name(self, node):
163 """All assignments to names go through this function."""
164 if node.ctx in ('store', 'param'):
165 self.identifiers.declared_locally.add(node.name)
166 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200167 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200168 self.identifiers.undeclared.add(node.name)
169
Armin Ronacherd55ab532008-04-09 16:13:39 +0200170 def visit_Filter(self, node):
Armin Ronacher449167d2008-04-11 17:55:05 +0200171 self.generic_visit(node)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200172 self.identifiers.filters.add(node.name)
173
174 def visit_Test(self, node):
175 self.generic_visit(node)
176 self.identifiers.tests.add(node.name)
Christoph Hack65642a52008-04-08 14:46:56 +0200177
Armin Ronachere791c2a2008-04-07 18:39:54 +0200178 def visit_Macro(self, node):
179 """Macros set local."""
180 self.identifiers.declared_locally.add(node.name)
181
Armin Ronacherf059ec12008-04-11 22:21:00 +0200182 def visit_Include(self, node):
183 """Some includes set local."""
184 self.generic_visit(node)
185 if node.target is not None:
186 self.identifiers.declared_locally.add(node.target)
187
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200188 def visit_Assign(self, node):
189 """Visit assignments in the correct order."""
190 self.visit(node.node)
191 self.visit(node.target)
192
Armin Ronachere791c2a2008-04-07 18:39:54 +0200193 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200194 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200195 visit_For = lambda s, n: None
196
197
Armin Ronacher75cfb862008-04-11 13:47:22 +0200198class CompilerExit(Exception):
199 """Raised if the compiler encountered a situation where it just
200 doesn't make sense to further process the code. Any block that
201 raises such an exception is not further processed."""
202
203
Armin Ronachere791c2a2008-04-07 18:39:54 +0200204class CodeGenerator(NodeVisitor):
205
Christoph Hack65642a52008-04-08 14:46:56 +0200206 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200207 if stream is None:
208 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200209 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200210 self.is_child = is_child
211 self.filename = filename
212 self.stream = stream
213 self.blocks = {}
214 self.indentation = 0
215 self.new_lines = 0
216 self.last_identifier = 0
Armin Ronacher7fb38972008-04-11 13:54:28 +0200217 self.extends_so_far = 0
Armin Ronacher75cfb862008-04-11 13:47:22 +0200218 self.has_known_extends = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200219 self._last_line = 0
220 self._first_write = True
221
222 def temporary_identifier(self):
223 self.last_identifier += 1
224 return 't%d' % self.last_identifier
225
226 def indent(self):
227 self.indentation += 1
228
Armin Ronacher8efc5222008-04-08 14:47:40 +0200229 def outdent(self, step=1):
230 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200231
232 def blockvisit(self, nodes, frame, force_generator=False):
233 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200234 if force_generator and frame.buffer is None:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200235 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200236 try:
237 for node in nodes:
238 self.visit(node, frame)
239 except CompilerExit:
240 pass
Armin Ronachere791c2a2008-04-07 18:39:54 +0200241 self.outdent()
242
243 def write(self, x):
244 if self.new_lines:
245 if not self._first_write:
246 self.stream.write('\n' * self.new_lines)
247 self._first_write = False
248 self.stream.write(' ' * self.indentation)
249 self.new_lines = 0
250 self.stream.write(x)
251
252 def writeline(self, x, node=None, extra=0):
253 self.newline(node, extra)
254 self.write(x)
255
256 def newline(self, node=None, extra=0):
257 self.new_lines = max(self.new_lines, 1 + extra)
258 if node is not None and node.lineno != self._last_line:
259 self.write('# line: %s' % node.lineno)
260 self.new_lines = 1
261 self._last_line = node.lineno
262
Armin Ronacher71082072008-04-12 14:19:36 +0200263 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200264 have_comma = have_comma and [True] or []
265 def touch_comma():
266 if have_comma:
267 self.write(', ')
268 else:
269 have_comma.append(True)
270
271 for arg in node.args:
272 touch_comma()
273 self.visit(arg, frame)
274 for kwarg in node.kwargs:
275 touch_comma()
276 self.visit(kwarg, frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200277 if extra_kwargs is not None:
278 touch_comma()
279 self.write(extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200280 if node.dyn_args:
281 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200282 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200283 self.visit(node.dyn_args, frame)
284 if node.dyn_kwargs:
285 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200286 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200287 self.visit(node.dyn_kwargs, frame)
288
Armin Ronachere791c2a2008-04-07 18:39:54 +0200289 def pull_locals(self, frame, no_indent=False):
290 if not no_indent:
291 self.indent()
292 for name in frame.identifiers.undeclared:
293 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200294 for name in frame.identifiers.filters:
295 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200296 for name in frame.identifiers.tests:
297 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200298 if not no_indent:
299 self.outdent()
300
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200301 def collect_shadowed(self, frame):
302 # make sure we "backup" overridden, local identifiers
303 # TODO: we should probably optimize this and check if the
304 # identifier is in use afterwards.
305 aliases = {}
306 for name in frame.identifiers.find_shadowed():
307 aliases[name] = ident = self.temporary_identifier()
308 self.writeline('%s = l_%s' % (ident, name))
309 return aliases
310
Armin Ronacher71082072008-04-12 14:19:36 +0200311 def function_scoping(self, node, frame):
312 func_frame = frame.inner()
313 func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
314
315 # variables that are undeclared (accessed before declaration) and
316 # declared locally *and* part of an outside scope raise a template
317 # assertion error. Reason: we can't generate reasonable code from
318 # it without aliasing all the variables. XXX: alias them ^^
319 overriden_closure_vars = (
320 func_frame.identifiers.undeclared &
321 func_frame.identifiers.declared &
322 (func_frame.identifiers.declared_locally |
323 func_frame.identifiers.declared_parameter)
324 )
325 if overriden_closure_vars:
326 vars = ', '.join(sorted(overriden_closure_vars))
327 raise TemplateAssertionError('It\'s not possible to set and '
328 'access variables derived from '
329 'an outer scope! (affects: %s' %
330 vars, node.lineno, self.filename)
331
332 # remove variables from a closure from the frame's undeclared
333 # identifiers.
334 func_frame.identifiers.undeclared -= (
335 func_frame.identifiers.undeclared &
336 func_frame.identifiers.declared
337 )
338
339 func_frame.accesses_arguments = False
340 func_frame.accesses_caller = False
341 func_frame.arguments = args = ['l_' + x.name for x in node.args]
342
343 if 'arguments' in func_frame.identifiers.undeclared:
344 func_frame.accesses_arguments = True
345 func_frame.identifiers.add_special('arguments')
346 args.append('l_arguments')
347 if 'caller' in func_frame.identifiers.undeclared:
348 func_frame.accesses_caller = True
349 func_frame.identifiers.add_special('caller')
350 args.append('l_caller')
351 return func_frame
352
Armin Ronachere791c2a2008-04-07 18:39:54 +0200353 # -- Visitors
354
355 def visit_Template(self, node, frame=None):
356 assert frame is None, 'no root frame allowed'
357 self.writeline('from jinja2.runtime import *')
358 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200359
Armin Ronacher75cfb862008-04-11 13:47:22 +0200360 # do we have an extends tag at all? If not, we can save some
361 # overhead by just not processing any inheritance code.
362 have_extends = node.find(nodes.Extends) is not None
363
Armin Ronacher8edbe492008-04-10 20:43:43 +0200364 # find all blocks
365 for block in node.find_all(nodes.Block):
366 if block.name in self.blocks:
367 raise TemplateAssertionError('block %r defined twice' %
368 block.name, block.lineno,
369 self.filename)
370 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200371
Armin Ronacher8efc5222008-04-08 14:47:40 +0200372 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200373 self.writeline('def root(globals, environment=environment'
374 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200375 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200376 self.writeline('context = TemplateContext(globals, %r, blocks'
377 ', standalone)' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200378 if have_extends:
379 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200380 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200381
382 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200383 frame = Frame()
384 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200385 frame.toplevel = frame.rootlevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200386 self.pull_locals(frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200387 self.indent()
388 self.writeline('yield context')
389 self.outdent()
390 self.blockvisit(node.body, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200391
Armin Ronacher8efc5222008-04-08 14:47:40 +0200392 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200393 if have_extends:
394 if not self.has_known_extends:
395 self.indent()
396 self.writeline('if parent_root is not None:')
397 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200398 self.writeline('stream = parent_root(context)')
399 self.writeline('stream.next()')
400 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200401 self.indent()
402 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200403 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200404
405 # at this point we now have the blocks collected and can visit them too.
406 for name, block in self.blocks.iteritems():
407 block_frame = Frame()
408 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200409 block_frame.block = name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200410 self.writeline('def block_%s(context, environment=environment):'
411 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200412 self.pull_locals(block_frame)
413 self.blockvisit(block.body, block_frame, True)
414
Armin Ronacher75cfb862008-04-11 13:47:22 +0200415 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
416 for x in self.blocks), extra=1)
417
Armin Ronachere791c2a2008-04-07 18:39:54 +0200418 def visit_Block(self, node, frame):
419 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200420 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200421 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200422 # if we know that we are a child template, there is no need to
423 # check if we are one
424 if self.has_known_extends:
425 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200426 if self.extends_so_far > 0:
427 self.writeline('if parent_root is None:')
428 self.indent()
429 level += 1
430 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200431 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200432 if frame.buffer is None:
433 self.writeline('yield event')
434 else:
435 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200436 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200437
438 def visit_Extends(self, node, frame):
439 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200440 if not frame.toplevel:
441 raise TemplateAssertionError('cannot use extend from a non '
442 'top-level scope', node.lineno,
443 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200444
Armin Ronacher7fb38972008-04-11 13:54:28 +0200445 # if the number of extends statements in general is zero so
446 # far, we don't have to add a check if something extended
447 # the template before this one.
448 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200449
Armin Ronacher7fb38972008-04-11 13:54:28 +0200450 # if we have a known extends we just add a template runtime
451 # error into the generated code. We could catch that at compile
452 # time too, but i welcome it not to confuse users by throwing the
453 # same error at different times just "because we can".
454 if not self.has_known_extends:
455 self.writeline('if parent_root is not None:')
456 self.indent()
457 self.writeline('raise TemplateRuntimeError(%r)' %
458 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200459
Armin Ronacher7fb38972008-04-11 13:54:28 +0200460 # if we have a known extends already we don't need that code here
461 # as we know that the template execution will end here.
462 if self.has_known_extends:
463 raise CompilerExit()
464 self.outdent()
465
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200466 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200467 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200468 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200469
470 # if this extends statement was in the root level we can take
471 # advantage of that information and simplify the generated code
472 # in the top level from this point onwards
473 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200474
Armin Ronacher7fb38972008-04-11 13:54:28 +0200475 # and now we have one more
476 self.extends_so_far += 1
477
Armin Ronacherf059ec12008-04-11 22:21:00 +0200478 def visit_Include(self, node, frame):
479 """Handles includes."""
480 # simpled include is include into a variable. This kind of
481 # include works the same on every level, so we handle it first.
482 if node.target is not None:
483 self.writeline('l_%s = ' % node.target, node)
484 if frame.toplevel:
485 self.write('context[%r] = ' % node.target)
486 self.write('IncludedTemplate(environment, context, ')
487 self.visit(node.template, frame)
488 self.write(')')
489 return
490
491 self.writeline('included_stream = environment.get_template(', node)
492 self.visit(node.template, frame)
493 self.write(').root_render_func(context, standalone=True)')
494 self.writeline('included_context = included_stream.next()')
495 self.writeline('for event in included_stream:')
496 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200497 if frame.buffer is None:
498 self.writeline('yield event')
499 else:
500 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200501 self.outdent()
502
503 # if we have a toplevel include the exported variables are copied
504 # into the current context without exporting them. context.udpate
505 # does *not* mark the variables as exported
506 if frame.toplevel:
507 self.writeline('context.update(included_context.get_exported())')
508
Armin Ronachere791c2a2008-04-07 18:39:54 +0200509 def visit_For(self, node, frame):
510 loop_frame = frame.inner()
511 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200512 extended_loop = bool(node.else_) or \
513 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200514 if extended_loop:
515 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200516
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200517 aliases = self.collect_shadowed(loop_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200518 self.pull_locals(loop_frame, True)
519
520 self.newline(node)
521 if node.else_:
522 self.writeline('l_loop = None')
523 self.write('for ')
524 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200525 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200526 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200527 if 'loop' in aliases:
528 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200529 self.write(extended_loop and '):' or ':')
530 self.blockvisit(node.body, loop_frame)
531
532 if node.else_:
533 self.writeline('if l_loop is None:')
534 self.blockvisit(node.else_, loop_frame)
535
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200536 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200537 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200538 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200539
540 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200541 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200542 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200543 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200544 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200545 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200546 if node.else_:
547 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200548 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200549
Armin Ronacher8efc5222008-04-08 14:47:40 +0200550 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200551 macro_frame = self.function_scoping(node, frame)
552 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200553 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200554 self.pull_locals(macro_frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200555 self.blockvisit(node.body, macro_frame, True)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200556 self.newline()
557 if frame.toplevel:
558 self.write('context[%r] = ' % node.name)
559 arg_tuple = ', '.join(repr(x.name) for x in node.args)
560 if len(node.args) == 1:
561 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200562 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
563 arg_tuple))
564 for arg in node.defaults:
565 self.visit(arg)
566 self.write(', ')
Armin Ronacher71082072008-04-12 14:19:36 +0200567 self.write('), %r, %r)' % (
568 macro_frame.accesses_arguments,
569 macro_frame.accesses_caller
570 ))
571
572 def visit_CallBlock(self, node, frame):
573 call_frame = self.function_scoping(node, frame)
574 args = call_frame.arguments
575 self.writeline('def call(%s):' % ', '.join(args), node)
576 self.blockvisit(node.body, call_frame, node)
577 arg_tuple = ', '.join(repr(x.name) for x in node.args)
578 if len(node.args) == 1:
579 arg_tuple += ','
580 self.writeline('caller = Macro(call, None, (%s), (' % arg_tuple)
581 for arg in node.defaults:
582 self.visit(arg)
583 self.write(', ')
584 self.write('), %r, False)' % call_frame.accesses_arguments)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200585 if frame.buffer is None:
586 self.writeline('yield ', node)
587 else:
588 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200589 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200590 if frame.buffer is not None:
591 self.write(')')
592
593 def visit_FilterBlock(self, node, frame):
594 filter_frame = frame.inner()
595 filter_frame.inspect(node.iter_child_nodes())
596
597 aliases = self.collect_shadowed(filter_frame)
598 self.pull_locals(filter_frame, True)
599 filter_frame.buffer = buf = self.temporary_identifier()
600
601 self.writeline('%s = []' % buf, node)
602 for child in node.body:
603 self.visit(child, filter_frame)
604
605 if frame.buffer is None:
606 self.writeline('yield ', node)
607 else:
608 self.writeline('%s.append(' % frame.buffer, node)
609 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
610 if frame.buffer is not None:
611 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200612
Armin Ronachere791c2a2008-04-07 18:39:54 +0200613 def visit_ExprStmt(self, node, frame):
614 self.newline(node)
615 self.visit(node, frame)
616
617 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200618 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200619 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200620 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200621
Armin Ronacher75cfb862008-04-11 13:47:22 +0200622 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200623 if self.environment.finalize is unicode:
624 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200625 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200626 else:
627 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200628 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200629
Armin Ronacher7fb38972008-04-11 13:54:28 +0200630 # if we are in the toplevel scope and there was already an extends
631 # statement we have to add a check that disables our yield(s) here
632 # so that they don't appear in the output.
633 outdent_later = False
634 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200635 self.writeline('if parent_root is None:')
636 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200637 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200638
Armin Ronachere791c2a2008-04-07 18:39:54 +0200639 # try to evaluate as many chunks as possible into a static
640 # string at compile time.
641 body = []
642 for child in node.nodes:
643 try:
644 const = unicode(child.as_const())
645 except:
646 body.append(child)
647 continue
648 if body and isinstance(body[-1], list):
649 body[-1].append(const)
650 else:
651 body.append([const])
652
653 # if we have less than 3 nodes we just yield them
654 if len(body) < 3:
655 for item in body:
656 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200657 val = repr(u''.join(item))
658 if frame.buffer is None:
659 self.writeline('yield ' + val)
660 else:
661 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200662 else:
663 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200664 if frame.buffer is None:
665 self.write('yield ')
666 else:
667 self.write('%s.append(' % frame.buffer)
668 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200669 self.visit(item, frame)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200670 self.write(')' * (1 + frame.buffer is not None))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200671
672 # otherwise we create a format string as this is faster in that case
673 else:
674 format = []
675 arguments = []
676 for item in body:
677 if isinstance(item, list):
678 format.append(u''.join(item).replace('%', '%%'))
679 else:
680 format.append('%s')
681 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200682 if frame.buffer is None:
683 self.writeline('yield ')
684 else:
685 self.writeline('%s.append(' % frame.buffer)
686 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200687 idx = -1
688 for idx, argument in enumerate(arguments):
689 if idx:
690 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200691 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200692 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200693 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200694 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200695 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200696 self.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200697 if frame.buffer is not None:
698 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200699
Armin Ronacher7fb38972008-04-11 13:54:28 +0200700 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200701 self.outdent()
702
Armin Ronacher8efc5222008-04-08 14:47:40 +0200703 def visit_Assign(self, node, frame):
704 self.newline(node)
705 # toplevel assignments however go into the local namespace and
706 # the current template's context. We create a copy of the frame
707 # here and add a set so that the Name visitor can add the assigned
708 # names here.
709 if frame.toplevel:
710 assignment_frame = frame.copy()
711 assignment_frame.assigned_names = set()
712 else:
713 assignment_frame = frame
714 self.visit(node.target, assignment_frame)
715 self.write(' = ')
716 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200717
718 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200719 if frame.toplevel:
720 for name in assignment_frame.assigned_names:
721 self.writeline('context[%r] = l_%s' % (name, name))
722
Armin Ronachere791c2a2008-04-07 18:39:54 +0200723 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200724 if frame.toplevel and node.ctx == 'store':
725 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200726 self.write('l_' + node.name)
727
728 def visit_Const(self, node, frame):
729 val = node.value
730 if isinstance(val, float):
731 # XXX: add checks for infinity and nan
732 self.write(str(val))
733 else:
734 self.write(repr(val))
735
Armin Ronacher8efc5222008-04-08 14:47:40 +0200736 def visit_Tuple(self, node, frame):
737 self.write('(')
738 idx = -1
739 for idx, item in enumerate(node.items):
740 if idx:
741 self.write(', ')
742 self.visit(item, frame)
743 self.write(idx == 0 and ',)' or ')')
744
Armin Ronacher8edbe492008-04-10 20:43:43 +0200745 def visit_List(self, node, frame):
746 self.write('[')
747 for idx, item in enumerate(node.items):
748 if idx:
749 self.write(', ')
750 self.visit(item, frame)
751 self.write(']')
752
753 def visit_Dict(self, node, frame):
754 self.write('{')
755 for idx, item in enumerate(node.items):
756 if idx:
757 self.write(', ')
758 self.visit(item.key, frame)
759 self.write(': ')
760 self.visit(item.value, frame)
761 self.write('}')
762
Armin Ronachere791c2a2008-04-07 18:39:54 +0200763 def binop(operator):
764 def visitor(self, node, frame):
765 self.write('(')
766 self.visit(node.left, frame)
767 self.write(' %s ' % operator)
768 self.visit(node.right, frame)
769 self.write(')')
770 return visitor
771
772 def uaop(operator):
773 def visitor(self, node, frame):
774 self.write('(' + operator)
775 self.visit(node.node)
776 self.write(')')
777 return visitor
778
779 visit_Add = binop('+')
780 visit_Sub = binop('-')
781 visit_Mul = binop('*')
782 visit_Div = binop('/')
783 visit_FloorDiv = binop('//')
784 visit_Pow = binop('**')
785 visit_Mod = binop('%')
786 visit_And = binop('and')
787 visit_Or = binop('or')
788 visit_Pos = uaop('+')
789 visit_Neg = uaop('-')
790 visit_Not = uaop('not ')
791 del binop, uaop
792
793 def visit_Compare(self, node, frame):
794 self.visit(node.expr, frame)
795 for op in node.ops:
796 self.visit(op, frame)
797
798 def visit_Operand(self, node, frame):
799 self.write(' %s ' % operators[node.op])
800 self.visit(node.expr, frame)
801
802 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200803 if isinstance(node.arg, nodes.Slice):
804 self.visit(node.node, frame)
805 self.write('[')
806 self.visit(node.arg, frame)
807 self.write(']')
808 return
809 try:
810 const = node.arg.as_const()
811 have_const = True
812 except nodes.Impossible:
813 have_const = False
814 if have_const:
815 if isinstance(const, (int, long, float)):
816 self.visit(node.node, frame)
817 self.write('[%s]' % const)
818 return
819 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200820 self.visit(node.node, frame)
821 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200822 if have_const:
823 self.write(repr(const))
824 else:
825 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200826 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200827
828 def visit_Slice(self, node, frame):
829 if node.start is not None:
830 self.visit(node.start, frame)
831 self.write(':')
832 if node.stop is not None:
833 self.visit(node.stop, frame)
834 if node.step is not None:
835 self.write(':')
836 self.visit(node.step, frame)
837
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200838 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200839 self.write('f_%s(' % node.name)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200840 if initial is not None:
841 self.write(initial)
842 else:
843 func = self.environment.filters.get(node.name)
844 if getattr(func, 'contextfilter', False):
845 self.write('context, ')
846 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200847 self.signature(node, frame)
848 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200849
850 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200851 self.write('t_%s(' % node.name)
852 func = self.environment.tests.get(node.name)
853 if getattr(func, 'contexttest', False):
854 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200855 self.visit(node.node, frame)
856 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200857 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200858
Armin Ronacher71082072008-04-12 14:19:36 +0200859 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200860 self.visit(node.node, frame)
861 self.write('(')
Armin Ronacher71082072008-04-12 14:19:36 +0200862 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200863 self.write(')')
864
865 def visit_Keyword(self, node, frame):
866 self.visit(node.key, frame)
867 self.write('=')
868 self.visit(node.value, frame)