blob: 3c2347d65c5dda2ba8dccb1630d5359e4a47778a [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
Armin Ronacher3d8b7842008-04-13 13:16:50 +020032try:
33 exec '(0 if 0 else 0)'
34except SyntaxError:
35 have_condexpr = False
36else:
37 have_condexpr = True
38
39
Christoph Hack65642a52008-04-08 14:46:56 +020040def generate(node, environment, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020041 """Generate the python source for a node tree."""
Armin Ronachere791c2a2008-04-07 18:39:54 +020042 is_child = node.find(nodes.Extends) is not None
Christoph Hack65642a52008-04-08 14:46:56 +020043 generator = CodeGenerator(environment, is_child, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020044 generator.visit(node)
45 if stream is None:
46 return generator.stream.getvalue()
47
48
Armin Ronacher4dfc9752008-04-09 15:03:29 +020049def has_safe_repr(value):
50 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020051 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020052 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020053 if isinstance(value, (bool, int, long, float, complex, basestring,
54 StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020055 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020056 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020057 for item in value:
58 if not has_safe_repr(item):
59 return False
60 return True
61 elif isinstance(value, dict):
62 for key, value in value.iteritems():
63 if not has_safe_repr(key):
64 return False
65 if not has_safe_repr(value):
66 return False
67 return True
68 return False
69
70
Armin Ronachere791c2a2008-04-07 18:39:54 +020071class Identifiers(object):
72 """Tracks the status of identifiers in frames."""
73
74 def __init__(self):
75 # variables that are known to be declared (probably from outer
76 # frames or because they are special for the frame)
77 self.declared = set()
78
79 # names that are accessed without being explicitly declared by
80 # this one or any of the outer scopes. Names can appear both in
81 # declared and undeclared.
82 self.undeclared = set()
83
84 # names that are declared locally
85 self.declared_locally = set()
86
87 # names that are declared by parameters
88 self.declared_parameter = set()
89
Armin Ronacherf059ec12008-04-11 22:21:00 +020090 # filters/tests that are referenced
Armin Ronacherd4c64f72008-04-11 17:15:29 +020091 self.filters = set()
Armin Ronacherf059ec12008-04-11 22:21:00 +020092 self.tests = set()
Christoph Hack65642a52008-04-08 14:46:56 +020093
Armin Ronachere791c2a2008-04-07 18:39:54 +020094 def add_special(self, name):
95 """Register a special name like `loop`."""
96 self.undeclared.discard(name)
97 self.declared.add(name)
98
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020099 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200100 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200101 if name in self.declared_locally or name in self.declared_parameter:
102 return True
103 if local_only:
104 return False
105 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +0200106
107 def find_shadowed(self):
108 """Find all the shadowed names."""
109 return self.declared & (self.declared_locally | self.declared_parameter)
110
111
112class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200113 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200114
115 def __init__(self, parent=None):
116 self.identifiers = Identifiers()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200117 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200118 self.toplevel = False
Armin Ronacher75cfb862008-04-11 13:47:22 +0200119 # the root frame is basically just the outermost frame, so no if
120 # conditions. This information is used to optimize inheritance
121 # situations.
122 self.rootlevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200123 self.parent = parent
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200124 self.buffer = None
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200125 self.name_overrides = {}
Armin Ronacher8efc5222008-04-08 14:47:40 +0200126 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200127 if parent is not None:
128 self.identifiers.declared.update(
129 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200130 parent.identifiers.declared_locally |
131 parent.identifiers.declared_parameter
132 )
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200133 self.buffer = parent.buffer
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200134 self.name_overrides = parent.name_overrides.copy()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200135
Armin Ronacher8efc5222008-04-08 14:47:40 +0200136 def copy(self):
137 """Create a copy of the current one."""
138 rv = copy(self)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200139 rv.identifiers = copy(self.identifiers)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200140 rv.name_overrides = self.name_overrides.copy()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200141 return rv
142
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200143 def inspect(self, nodes, hard_scope=False):
144 """Walk the node and check for identifiers. If the scope
145 is hard (eg: enforce on a python level) overrides from outer
146 scopes are tracked differently.
147 """
148 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200149 for node in nodes:
150 visitor.visit(node)
151
152 def inner(self):
153 """Return an inner frame."""
154 return Frame(self)
155
Armin Ronacher75cfb862008-04-11 13:47:22 +0200156 def soft(self):
157 """Return a soft frame. A soft frame may not be modified as
158 standalone thing as it shares the resources with the frame it
159 was created of, but it's not a rootlevel frame any longer.
160 """
161 rv = copy(self)
162 rv.rootlevel = False
163 return rv
164
Armin Ronachere791c2a2008-04-07 18:39:54 +0200165
166class FrameIdentifierVisitor(NodeVisitor):
167 """A visitor for `Frame.inspect`."""
168
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200169 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200170 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200171 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200172
173 def visit_Name(self, node):
174 """All assignments to names go through this function."""
175 if node.ctx in ('store', 'param'):
176 self.identifiers.declared_locally.add(node.name)
177 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200178 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200179 self.identifiers.undeclared.add(node.name)
180
Armin Ronacherd55ab532008-04-09 16:13:39 +0200181 def visit_Filter(self, node):
Armin Ronacher449167d2008-04-11 17:55:05 +0200182 self.generic_visit(node)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200183 self.identifiers.filters.add(node.name)
184
185 def visit_Test(self, node):
186 self.generic_visit(node)
187 self.identifiers.tests.add(node.name)
Christoph Hack65642a52008-04-08 14:46:56 +0200188
Armin Ronachere791c2a2008-04-07 18:39:54 +0200189 def visit_Macro(self, node):
190 """Macros set local."""
191 self.identifiers.declared_locally.add(node.name)
192
Armin Ronacherf059ec12008-04-11 22:21:00 +0200193 def visit_Include(self, node):
194 """Some includes set local."""
195 self.generic_visit(node)
196 if node.target is not None:
197 self.identifiers.declared_locally.add(node.target)
198
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200199 def visit_Assign(self, node):
200 """Visit assignments in the correct order."""
201 self.visit(node.node)
202 self.visit(node.target)
203
Armin Ronachere791c2a2008-04-07 18:39:54 +0200204 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200205 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200206 visit_For = lambda s, n: None
207
208
Armin Ronacher75cfb862008-04-11 13:47:22 +0200209class CompilerExit(Exception):
210 """Raised if the compiler encountered a situation where it just
211 doesn't make sense to further process the code. Any block that
212 raises such an exception is not further processed."""
213
214
Armin Ronachere791c2a2008-04-07 18:39:54 +0200215class CodeGenerator(NodeVisitor):
216
Christoph Hack65642a52008-04-08 14:46:56 +0200217 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200218 if stream is None:
219 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200220 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200221 self.is_child = is_child
222 self.filename = filename
223 self.stream = stream
224 self.blocks = {}
225 self.indentation = 0
226 self.new_lines = 0
227 self.last_identifier = 0
Armin Ronacher7fb38972008-04-11 13:54:28 +0200228 self.extends_so_far = 0
Armin Ronacher75cfb862008-04-11 13:47:22 +0200229 self.has_known_extends = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200230 self._last_line = 0
231 self._first_write = True
232
233 def temporary_identifier(self):
234 self.last_identifier += 1
235 return 't%d' % self.last_identifier
236
237 def indent(self):
238 self.indentation += 1
239
Armin Ronacher8efc5222008-04-08 14:47:40 +0200240 def outdent(self, step=1):
241 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200242
Armin Ronacher625215e2008-04-13 16:31:08 +0200243 def blockvisit(self, nodes, frame, indent=True, force_generator=True):
244 if indent:
245 self.indent()
246 if frame.buffer is None and force_generator:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200247 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200248 try:
249 for node in nodes:
250 self.visit(node, frame)
251 except CompilerExit:
252 pass
Armin Ronacher625215e2008-04-13 16:31:08 +0200253 if indent:
254 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200255
256 def write(self, x):
257 if self.new_lines:
258 if not self._first_write:
259 self.stream.write('\n' * self.new_lines)
260 self._first_write = False
261 self.stream.write(' ' * self.indentation)
262 self.new_lines = 0
263 self.stream.write(x)
264
265 def writeline(self, x, node=None, extra=0):
266 self.newline(node, extra)
267 self.write(x)
268
269 def newline(self, node=None, extra=0):
270 self.new_lines = max(self.new_lines, 1 + extra)
271 if node is not None and node.lineno != self._last_line:
272 self.write('# line: %s' % node.lineno)
273 self.new_lines = 1
274 self._last_line = node.lineno
275
Armin Ronacher71082072008-04-12 14:19:36 +0200276 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200277 have_comma = have_comma and [True] or []
278 def touch_comma():
279 if have_comma:
280 self.write(', ')
281 else:
282 have_comma.append(True)
283
284 for arg in node.args:
285 touch_comma()
286 self.visit(arg, frame)
287 for kwarg in node.kwargs:
288 touch_comma()
289 self.visit(kwarg, frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200290 if extra_kwargs is not None:
291 touch_comma()
292 self.write(extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200293 if node.dyn_args:
294 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200295 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200296 self.visit(node.dyn_args, frame)
297 if node.dyn_kwargs:
298 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200299 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200300 self.visit(node.dyn_kwargs, frame)
301
Armin Ronacher625215e2008-04-13 16:31:08 +0200302 def pull_locals(self, frame, indent=True):
303 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200304 self.indent()
305 for name in frame.identifiers.undeclared:
306 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200307 for name in frame.identifiers.filters:
308 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200309 for name in frame.identifiers.tests:
310 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronacher625215e2008-04-13 16:31:08 +0200311 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200312 self.outdent()
313
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200314 def collect_shadowed(self, frame):
315 # make sure we "backup" overridden, local identifiers
316 # TODO: we should probably optimize this and check if the
317 # identifier is in use afterwards.
318 aliases = {}
319 for name in frame.identifiers.find_shadowed():
320 aliases[name] = ident = self.temporary_identifier()
321 self.writeline('%s = l_%s' % (ident, name))
322 return aliases
323
Armin Ronacher71082072008-04-12 14:19:36 +0200324 def function_scoping(self, node, frame):
325 func_frame = frame.inner()
326 func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
327
328 # variables that are undeclared (accessed before declaration) and
329 # declared locally *and* part of an outside scope raise a template
330 # assertion error. Reason: we can't generate reasonable code from
331 # it without aliasing all the variables. XXX: alias them ^^
332 overriden_closure_vars = (
333 func_frame.identifiers.undeclared &
334 func_frame.identifiers.declared &
335 (func_frame.identifiers.declared_locally |
336 func_frame.identifiers.declared_parameter)
337 )
338 if overriden_closure_vars:
339 vars = ', '.join(sorted(overriden_closure_vars))
340 raise TemplateAssertionError('It\'s not possible to set and '
341 'access variables derived from '
342 'an outer scope! (affects: %s' %
343 vars, node.lineno, self.filename)
344
345 # remove variables from a closure from the frame's undeclared
346 # identifiers.
347 func_frame.identifiers.undeclared -= (
348 func_frame.identifiers.undeclared &
349 func_frame.identifiers.declared
350 )
351
352 func_frame.accesses_arguments = False
353 func_frame.accesses_caller = False
354 func_frame.arguments = args = ['l_' + x.name for x in node.args]
355
356 if 'arguments' in func_frame.identifiers.undeclared:
357 func_frame.accesses_arguments = True
358 func_frame.identifiers.add_special('arguments')
359 args.append('l_arguments')
360 if 'caller' in func_frame.identifiers.undeclared:
361 func_frame.accesses_caller = True
362 func_frame.identifiers.add_special('caller')
363 args.append('l_caller')
364 return func_frame
365
Armin Ronachere791c2a2008-04-07 18:39:54 +0200366 # -- Visitors
367
368 def visit_Template(self, node, frame=None):
369 assert frame is None, 'no root frame allowed'
370 self.writeline('from jinja2.runtime import *')
371 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200372
Armin Ronacher75cfb862008-04-11 13:47:22 +0200373 # do we have an extends tag at all? If not, we can save some
374 # overhead by just not processing any inheritance code.
375 have_extends = node.find(nodes.Extends) is not None
376
Armin Ronacher8edbe492008-04-10 20:43:43 +0200377 # find all blocks
378 for block in node.find_all(nodes.Block):
379 if block.name in self.blocks:
380 raise TemplateAssertionError('block %r defined twice' %
381 block.name, block.lineno,
382 self.filename)
383 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200384
Armin Ronacher8efc5222008-04-08 14:47:40 +0200385 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200386 self.writeline('def root(globals, environment=environment'
387 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200388 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200389 self.writeline('context = TemplateContext(globals, %r, blocks'
390 ', standalone)' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200391 if have_extends:
392 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200393 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200394
395 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200396 frame = Frame()
397 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200398 frame.toplevel = frame.rootlevel = True
Armin Ronacherf059ec12008-04-11 22:21:00 +0200399 self.indent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200400 self.pull_locals(frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200401 self.writeline('yield context')
Armin Ronacher625215e2008-04-13 16:31:08 +0200402 self.blockvisit(node.body, frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200403 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200404
Armin Ronacher8efc5222008-04-08 14:47:40 +0200405 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200406 if have_extends:
407 if not self.has_known_extends:
408 self.indent()
409 self.writeline('if parent_root is not None:')
410 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200411 self.writeline('stream = parent_root(context)')
412 self.writeline('stream.next()')
413 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200414 self.indent()
415 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200416 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200417
418 # at this point we now have the blocks collected and can visit them too.
419 for name, block in self.blocks.iteritems():
420 block_frame = Frame()
421 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200422 block_frame.block = name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200423 self.writeline('def block_%s(context, environment=environment):'
424 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200425 self.pull_locals(block_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200426 self.blockvisit(block.body, block_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200427
Armin Ronacher75cfb862008-04-11 13:47:22 +0200428 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
429 for x in self.blocks), extra=1)
430
Armin Ronachere791c2a2008-04-07 18:39:54 +0200431 def visit_Block(self, node, frame):
432 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200433 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200434 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200435 # if we know that we are a child template, there is no need to
436 # check if we are one
437 if self.has_known_extends:
438 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200439 if self.extends_so_far > 0:
440 self.writeline('if parent_root is None:')
441 self.indent()
442 level += 1
443 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200444 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200445 if frame.buffer is None:
446 self.writeline('yield event')
447 else:
448 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200449 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200450
451 def visit_Extends(self, node, frame):
452 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200453 if not frame.toplevel:
454 raise TemplateAssertionError('cannot use extend from a non '
455 'top-level scope', node.lineno,
456 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200457
Armin Ronacher7fb38972008-04-11 13:54:28 +0200458 # if the number of extends statements in general is zero so
459 # far, we don't have to add a check if something extended
460 # the template before this one.
461 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200462
Armin Ronacher7fb38972008-04-11 13:54:28 +0200463 # if we have a known extends we just add a template runtime
464 # error into the generated code. We could catch that at compile
465 # time too, but i welcome it not to confuse users by throwing the
466 # same error at different times just "because we can".
467 if not self.has_known_extends:
468 self.writeline('if parent_root is not None:')
469 self.indent()
470 self.writeline('raise TemplateRuntimeError(%r)' %
471 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200472
Armin Ronacher7fb38972008-04-11 13:54:28 +0200473 # if we have a known extends already we don't need that code here
474 # as we know that the template execution will end here.
475 if self.has_known_extends:
476 raise CompilerExit()
477 self.outdent()
478
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200479 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200480 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200481 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200482
483 # if this extends statement was in the root level we can take
484 # advantage of that information and simplify the generated code
485 # in the top level from this point onwards
486 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200487
Armin Ronacher7fb38972008-04-11 13:54:28 +0200488 # and now we have one more
489 self.extends_so_far += 1
490
Armin Ronacherf059ec12008-04-11 22:21:00 +0200491 def visit_Include(self, node, frame):
492 """Handles includes."""
493 # simpled include is include into a variable. This kind of
494 # include works the same on every level, so we handle it first.
495 if node.target is not None:
496 self.writeline('l_%s = ' % node.target, node)
497 if frame.toplevel:
498 self.write('context[%r] = ' % node.target)
499 self.write('IncludedTemplate(environment, context, ')
500 self.visit(node.template, frame)
501 self.write(')')
502 return
503
504 self.writeline('included_stream = environment.get_template(', node)
505 self.visit(node.template, frame)
506 self.write(').root_render_func(context, standalone=True)')
507 self.writeline('included_context = included_stream.next()')
508 self.writeline('for event in included_stream:')
509 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200510 if frame.buffer is None:
511 self.writeline('yield event')
512 else:
513 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200514 self.outdent()
515
516 # if we have a toplevel include the exported variables are copied
517 # into the current context without exporting them. context.udpate
518 # does *not* mark the variables as exported
519 if frame.toplevel:
520 self.writeline('context.update(included_context.get_exported())')
521
Armin Ronachere791c2a2008-04-07 18:39:54 +0200522 def visit_For(self, node, frame):
523 loop_frame = frame.inner()
524 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200525 extended_loop = bool(node.else_) or \
526 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200527 if extended_loop:
528 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200529
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200530 aliases = self.collect_shadowed(loop_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200531 self.pull_locals(loop_frame, indent=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200532 if node.else_:
533 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200534
535 self.newline(node)
536 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200537 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200538 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200539
540 # the expression pointing to the parent loop. We make the
541 # undefined a bit more debug friendly at the same time.
542 parent_loop = 'loop' in aliases and aliases['loop'] \
543 or "Undefined('loop', extra=%r)" % \
544 'the filter section of a loop as well as the ' \
545 'else block doesn\'t have access to the special ' \
546 "'loop' variable of the current loop. Because " \
547 'there is no parent loop it\'s undefined.'
548
549 # if we have an extened loop and a node test, we filter in the
550 # "outer frame".
551 if extended_loop and node.test is not None:
552 self.write('(')
553 self.visit(node.target, loop_frame)
554 self.write(' for ')
555 self.visit(node.target, loop_frame)
556 self.write(' in ')
557 self.visit(node.iter, loop_frame)
558 self.write(' if (')
559 test_frame = loop_frame.copy()
560 test_frame.name_overrides['loop'] = parent_loop
561 self.visit(node.test, test_frame)
562 self.write('))')
563
564 else:
565 self.visit(node.iter, loop_frame)
566
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200567 if 'loop' in aliases:
568 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200569 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200570
571 # tests in not extended loops become a continue
572 if not extended_loop and node.test is not None:
573 self.indent()
574 self.writeline('if ')
575 self.visit(node.test)
576 self.write(':')
577 self.indent()
578 self.writeline('continue')
579 self.outdent(2)
580
Armin Ronacher625215e2008-04-13 16:31:08 +0200581 self.blockvisit(node.body, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200582
583 if node.else_:
584 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200585 self.indent()
586 self.writeline('l_loop = ' + parent_loop)
587 self.outdent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200588 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200589
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200590 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200591 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200592 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200593
594 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200595 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200596 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200597 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200598 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200599 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200600 if node.else_:
601 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200602 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200603
Armin Ronacher8efc5222008-04-08 14:47:40 +0200604 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200605 macro_frame = self.function_scoping(node, frame)
606 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200607 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200608 macro_frame.buffer = buf = self.temporary_identifier()
609 self.indent()
610 self.pull_locals(macro_frame, indent=False)
611 self.writeline('%s = []' % buf)
612 self.blockvisit(node.body, macro_frame, indent=False)
613 self.writeline("return TemplateData(u''.join(%s))" % buf)
614 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200615 self.newline()
616 if frame.toplevel:
617 self.write('context[%r] = ' % node.name)
618 arg_tuple = ', '.join(repr(x.name) for x in node.args)
619 if len(node.args) == 1:
620 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200621 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
622 arg_tuple))
623 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +0200624 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200625 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200626 self.write('), %s, %s)' % (
627 macro_frame.accesses_arguments and '1' or '0',
628 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +0200629 ))
630
631 def visit_CallBlock(self, node, frame):
632 call_frame = self.function_scoping(node, frame)
633 args = call_frame.arguments
634 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200635 call_frame.buffer = buf = self.temporary_identifier()
636 self.indent()
637 self.pull_locals(call_frame, indent=False)
638 self.writeline('%s = []' % buf)
639 self.blockvisit(node.body, call_frame, indent=False)
640 self.writeline("return TemplateData(u''.join(%s))" % buf)
641 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +0200642 arg_tuple = ', '.join(repr(x.name) for x in node.args)
643 if len(node.args) == 1:
644 arg_tuple += ','
645 self.writeline('caller = Macro(call, None, (%s), (' % arg_tuple)
646 for arg in node.defaults:
647 self.visit(arg)
648 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200649 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200650 if frame.buffer is None:
651 self.writeline('yield ', node)
652 else:
653 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200654 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200655 if frame.buffer is not None:
656 self.write(')')
657
658 def visit_FilterBlock(self, node, frame):
659 filter_frame = frame.inner()
660 filter_frame.inspect(node.iter_child_nodes())
661
662 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200663 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200664 filter_frame.buffer = buf = self.temporary_identifier()
665
666 self.writeline('%s = []' % buf, node)
667 for child in node.body:
668 self.visit(child, filter_frame)
669
670 if frame.buffer is None:
671 self.writeline('yield ', node)
672 else:
673 self.writeline('%s.append(' % frame.buffer, node)
674 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
675 if frame.buffer is not None:
676 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200677
Armin Ronachere791c2a2008-04-07 18:39:54 +0200678 def visit_ExprStmt(self, node, frame):
679 self.newline(node)
680 self.visit(node, frame)
681
682 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200683 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200684 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200685 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200686
Armin Ronacher75cfb862008-04-11 13:47:22 +0200687 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200688 if self.environment.finalize is unicode:
689 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200690 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200691 else:
692 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200693 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200694
Armin Ronacher7fb38972008-04-11 13:54:28 +0200695 # if we are in the toplevel scope and there was already an extends
696 # statement we have to add a check that disables our yield(s) here
697 # so that they don't appear in the output.
698 outdent_later = False
699 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200700 self.writeline('if parent_root is None:')
701 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200702 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200703
Armin Ronachere791c2a2008-04-07 18:39:54 +0200704 # try to evaluate as many chunks as possible into a static
705 # string at compile time.
706 body = []
707 for child in node.nodes:
708 try:
709 const = unicode(child.as_const())
710 except:
711 body.append(child)
712 continue
713 if body and isinstance(body[-1], list):
714 body[-1].append(const)
715 else:
716 body.append([const])
717
718 # if we have less than 3 nodes we just yield them
719 if len(body) < 3:
720 for item in body:
721 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200722 val = repr(u''.join(item))
723 if frame.buffer is None:
724 self.writeline('yield ' + val)
725 else:
726 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200727 else:
728 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200729 if frame.buffer is None:
730 self.write('yield ')
731 else:
732 self.write('%s.append(' % frame.buffer)
733 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200734 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200735 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200736
737 # otherwise we create a format string as this is faster in that case
738 else:
739 format = []
740 arguments = []
741 for item in body:
742 if isinstance(item, list):
743 format.append(u''.join(item).replace('%', '%%'))
744 else:
745 format.append('%s')
746 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200747 if frame.buffer is None:
748 self.writeline('yield ')
749 else:
750 self.writeline('%s.append(' % frame.buffer)
751 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200752 idx = -1
753 for idx, argument in enumerate(arguments):
754 if idx:
755 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200756 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200757 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200758 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200759 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200760 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200761 self.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200762 if frame.buffer is not None:
763 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200764
Armin Ronacher7fb38972008-04-11 13:54:28 +0200765 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200766 self.outdent()
767
Armin Ronacher8efc5222008-04-08 14:47:40 +0200768 def visit_Assign(self, node, frame):
769 self.newline(node)
770 # toplevel assignments however go into the local namespace and
771 # the current template's context. We create a copy of the frame
772 # here and add a set so that the Name visitor can add the assigned
773 # names here.
774 if frame.toplevel:
775 assignment_frame = frame.copy()
776 assignment_frame.assigned_names = set()
777 else:
778 assignment_frame = frame
779 self.visit(node.target, assignment_frame)
780 self.write(' = ')
781 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200782
783 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200784 if frame.toplevel:
785 for name in assignment_frame.assigned_names:
786 self.writeline('context[%r] = l_%s' % (name, name))
787
Armin Ronachere791c2a2008-04-07 18:39:54 +0200788 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200789 if node.ctx == 'store':
790 if frame.toplevel:
791 frame.assigned_names.add(node.name)
792 frame.name_overrides.pop(node.name, None)
793 elif node.ctx == 'load':
794 if node.name in frame.name_overrides:
795 self.write(frame.name_overrides[node.name])
796 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200797 self.write('l_' + node.name)
798
799 def visit_Const(self, node, frame):
800 val = node.value
801 if isinstance(val, float):
802 # XXX: add checks for infinity and nan
803 self.write(str(val))
804 else:
805 self.write(repr(val))
806
Armin Ronacher8efc5222008-04-08 14:47:40 +0200807 def visit_Tuple(self, node, frame):
808 self.write('(')
809 idx = -1
810 for idx, item in enumerate(node.items):
811 if idx:
812 self.write(', ')
813 self.visit(item, frame)
814 self.write(idx == 0 and ',)' or ')')
815
Armin Ronacher8edbe492008-04-10 20:43:43 +0200816 def visit_List(self, node, frame):
817 self.write('[')
818 for idx, item in enumerate(node.items):
819 if idx:
820 self.write(', ')
821 self.visit(item, frame)
822 self.write(']')
823
824 def visit_Dict(self, node, frame):
825 self.write('{')
826 for idx, item in enumerate(node.items):
827 if idx:
828 self.write(', ')
829 self.visit(item.key, frame)
830 self.write(': ')
831 self.visit(item.value, frame)
832 self.write('}')
833
Armin Ronachere791c2a2008-04-07 18:39:54 +0200834 def binop(operator):
835 def visitor(self, node, frame):
836 self.write('(')
837 self.visit(node.left, frame)
838 self.write(' %s ' % operator)
839 self.visit(node.right, frame)
840 self.write(')')
841 return visitor
842
843 def uaop(operator):
844 def visitor(self, node, frame):
845 self.write('(' + operator)
846 self.visit(node.node)
847 self.write(')')
848 return visitor
849
850 visit_Add = binop('+')
851 visit_Sub = binop('-')
852 visit_Mul = binop('*')
853 visit_Div = binop('/')
854 visit_FloorDiv = binop('//')
855 visit_Pow = binop('**')
856 visit_Mod = binop('%')
857 visit_And = binop('and')
858 visit_Or = binop('or')
859 visit_Pos = uaop('+')
860 visit_Neg = uaop('-')
861 visit_Not = uaop('not ')
862 del binop, uaop
863
864 def visit_Compare(self, node, frame):
865 self.visit(node.expr, frame)
866 for op in node.ops:
867 self.visit(op, frame)
868
869 def visit_Operand(self, node, frame):
870 self.write(' %s ' % operators[node.op])
871 self.visit(node.expr, frame)
872
873 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200874 if isinstance(node.arg, nodes.Slice):
875 self.visit(node.node, frame)
876 self.write('[')
877 self.visit(node.arg, frame)
878 self.write(']')
879 return
880 try:
881 const = node.arg.as_const()
882 have_const = True
883 except nodes.Impossible:
884 have_const = False
885 if have_const:
886 if isinstance(const, (int, long, float)):
887 self.visit(node.node, frame)
888 self.write('[%s]' % const)
889 return
890 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200891 self.visit(node.node, frame)
892 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200893 if have_const:
894 self.write(repr(const))
895 else:
896 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200897 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200898
899 def visit_Slice(self, node, frame):
900 if node.start is not None:
901 self.visit(node.start, frame)
902 self.write(':')
903 if node.stop is not None:
904 self.visit(node.stop, frame)
905 if node.step is not None:
906 self.write(':')
907 self.visit(node.step, frame)
908
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200909 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200910 self.write('f_%s(' % node.name)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200911 if initial is not None:
912 self.write(initial)
913 else:
914 func = self.environment.filters.get(node.name)
915 if getattr(func, 'contextfilter', False):
916 self.write('context, ')
917 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200918 self.signature(node, frame)
919 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200920
921 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200922 self.write('t_%s(' % node.name)
923 func = self.environment.tests.get(node.name)
924 if getattr(func, 'contexttest', False):
925 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200926 self.visit(node.node, frame)
927 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200928 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200929
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200930 def visit_CondExpr(self, node, frame):
931 if not have_condexpr:
932 self.write('((')
933 self.visit(node.test, frame)
934 self.write(') and (')
935 self.visit(node.expr1, frame)
936 self.write(',) or (')
937 self.visit(node.expr2, frame)
938 self.write(',))[0]')
939 else:
940 self.write('(')
941 self.visit(node.expr1, frame)
942 self.write(' if ')
943 self.visit(node.test, frame)
944 self.write(' else ')
945 self.visit(node.expr2, frame)
946 self.write(')')
947
Armin Ronacher71082072008-04-12 14:19:36 +0200948 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200949 self.visit(node.node, frame)
950 self.write('(')
Armin Ronacher71082072008-04-12 14:19:36 +0200951 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200952 self.write(')')
953
954 def visit_Keyword(self, node, frame):
955 self.visit(node.key, frame)
956 self.write('=')
957 self.visit(node.value, frame)