added loop filtering
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index e692184..e16617e 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -29,6 +29,14 @@
}
+try:
+ exec '(0 if 0 else 0)'
+except SyntaxError:
+ have_condexpr = False
+else:
+ have_condexpr = True
+
+
def generate(node, environment, filename, stream=None):
"""Generate the python source for a node tree."""
is_child = node.find(nodes.Extends) is not None
@@ -114,6 +122,7 @@
self.rootlevel = False
self.parent = parent
self.buffer = None
+ self.name_overrides = {}
self.block = parent and parent.block or None
if parent is not None:
self.identifiers.declared.update(
@@ -122,11 +131,13 @@
parent.identifiers.declared_parameter
)
self.buffer = parent.buffer
+ self.name_overrides = parent.name_overrides.copy()
def copy(self):
"""Create a copy of the current one."""
rv = copy(self)
rv.identifiers = copy(self.identifiers)
+ rv.name_overrides = self.name_overrides.copy()
return rv
def inspect(self, nodes, hard_scope=False):
@@ -516,21 +527,62 @@
aliases = self.collect_shadowed(loop_frame)
self.pull_locals(loop_frame, True)
-
- self.newline(node)
if node.else_:
self.writeline('l_loop = None')
- self.write('for ')
+
+ self.newline(node)
+ self.writeline('for ')
self.visit(node.target, loop_frame)
self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
- self.visit(node.iter, loop_frame)
+
+ # the expression pointing to the parent loop. We make the
+ # undefined a bit more debug friendly at the same time.
+ parent_loop = 'loop' in aliases and aliases['loop'] \
+ or "Undefined('loop', extra=%r)" % \
+ 'the filter section of a loop as well as the ' \
+ 'else block doesn\'t have access to the special ' \
+ "'loop' variable of the current loop. Because " \
+ 'there is no parent loop it\'s undefined.'
+
+ # if we have an extened loop and a node test, we filter in the
+ # "outer frame".
+ if extended_loop and node.test is not None:
+ self.write('(')
+ self.visit(node.target, loop_frame)
+ self.write(' for ')
+ self.visit(node.target, loop_frame)
+ self.write(' in ')
+ self.visit(node.iter, loop_frame)
+ self.write(' if (')
+ test_frame = loop_frame.copy()
+ test_frame.name_overrides['loop'] = parent_loop
+ self.visit(node.test, test_frame)
+ self.write('))')
+
+ else:
+ self.visit(node.iter, loop_frame)
+
if 'loop' in aliases:
self.write(', ' + aliases['loop'])
self.write(extended_loop and '):' or ':')
+
+ # tests in not extended loops become a continue
+ if not extended_loop and node.test is not None:
+ self.indent()
+ self.writeline('if ')
+ self.visit(node.test)
+ self.write(':')
+ self.indent()
+ self.writeline('continue')
+ self.outdent(2)
+
self.blockvisit(node.body, loop_frame)
if node.else_:
self.writeline('if l_loop is None:')
+ self.indent()
+ self.writeline('l_loop = ' + parent_loop)
+ self.outdent()
self.blockvisit(node.else_, loop_frame)
# reset the aliases if there are any.
@@ -667,7 +719,7 @@
self.write('%s.append(' % frame.buffer)
self.write(finalizer + '(')
self.visit(item, frame)
- self.write(')' * (1 + frame.buffer is not None))
+ self.write(')' * (1 + (frame.buffer is not None)))
# otherwise we create a format string as this is faster in that case
else:
@@ -721,8 +773,14 @@
self.writeline('context[%r] = l_%s' % (name, name))
def visit_Name(self, node, frame):
- if frame.toplevel and node.ctx == 'store':
- frame.assigned_names.add(node.name)
+ if node.ctx == 'store':
+ if frame.toplevel:
+ frame.assigned_names.add(node.name)
+ frame.name_overrides.pop(node.name, None)
+ elif node.ctx == 'load':
+ if node.name in frame.name_overrides:
+ self.write(frame.name_overrides[node.name])
+ return
self.write('l_' + node.name)
def visit_Const(self, node, frame):
@@ -856,6 +914,24 @@
self.signature(node, frame)
self.write(')')
+ def visit_CondExpr(self, node, frame):
+ if not have_condexpr:
+ self.write('((')
+ self.visit(node.test, frame)
+ self.write(') and (')
+ self.visit(node.expr1, frame)
+ self.write(',) or (')
+ self.visit(node.expr2, frame)
+ self.write(',))[0]')
+ else:
+ self.write('(')
+ self.visit(node.expr1, frame)
+ self.write(' if ')
+ self.visit(node.test, frame)
+ self.write(' else ')
+ self.visit(node.expr2, frame)
+ self.write(')')
+
def visit_Call(self, node, frame, extra_kwargs=None):
self.visit(node.node, frame)
self.write('(')