Permalink
Browse files

merged

--HG--
branch : trunk
  • Loading branch information...
2 parents 2751d94 + 180a1bd commit bfe7a6bb01499b7d8a9b3fd3ba7f7854856de3a5 @tux21b tux21b committed Apr 9, 2008
Showing with 95 additions and 17 deletions.
  1. +3 −1 jinja2/compiler.py
  2. +58 −12 jinja2/optimizer.py
  3. +31 −1 jinja2/runtime.py
  4. +3 −3 test_optimizer.py
View
@@ -315,8 +315,10 @@ def visit_For(self, node, frame):
self.writeline('l_loop = None')
self.write('for ')
self.visit(node.target, loop_frame)
- self.write(extended_loop and ', l_loop in looper(' or ' in ')
+ self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
self.visit(node.iter, loop_frame)
+ if 'loop' in aliases:
+ self.write(', ' + aliases['loop'])
self.write(extended_loop and '):' or ':')
self.blockvisit(node.body, loop_frame)
View
@@ -22,7 +22,7 @@
from copy import deepcopy
from jinja2 import nodes
from jinja2.visitor import NodeVisitor, NodeTransformer
-from jinja2.runtime import subscribe
+from jinja2.runtime import subscribe, LoopContext
class ContextStack(object):
@@ -39,6 +39,12 @@ def push(self):
def pop(self):
self.stack.pop()
+ def get(self, key, default=None):
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
def __getitem__(self, key):
for level in reversed(self.stack):
if key in level:
@@ -58,6 +64,9 @@ class Optimizer(NodeTransformer):
def __init__(self, environment):
self.environment = environment
+ def visit_Block(self, node, context):
+ return self.generic_visit(node, context.blank())
+
def visit_Filter(self, node, context):
"""Try to evaluate filters if possible."""
# XXX: nonconstant arguments? not-called visitors? generic visit!
@@ -77,18 +86,44 @@ def visit_For(self, node, context):
iterable = iter(self.visit(node.iter, context).as_const())
except (nodes.Impossible, TypeError):
return self.generic_visit(node, context)
+
+ parent = context.get('loop')
context.push()
result = []
- # XXX: tuple unpacking (for key, value in foo)
- target = node.target.name
iterated = False
- for item in iterable:
- context[target] = item
- result.extend(self.visit(n, context) for n in deepcopy(node.body))
- iterated = True
- if not iterated and node.else_:
- result.extend(self.visit(n, context) for n in deepcopy(node.else_))
- context.pop()
+
+ def assign(target, value):
+ if isinstance(target, nodes.Name):
+ context[target.name] = value
+ elif isinstance(target, nodes.Tuple):
+ try:
+ value = tuple(value)
+ except TypeError:
+ raise nodes.Impossible()
+ if len(target.items) != len(value):
+ raise nodes.Impossible()
+ for name, val in zip(target.items, value):
+ assign(name, val)
+ else:
+ raise AssertionError('unexpected assignable node')
+
+ # XXX: not covered cases:
+ # - item is accessed by dynamic part in the iteration
+ try:
+ try:
+ for loop, item in LoopContext(iterable, parent):
+ context['loop'] = loop
+ assign(node.target, item)
+ result.extend(self.visit(n, context)
+ for n in deepcopy(node.body))
+ iterated = True
+ if not iterated and node.else_:
+ result.extend(self.visit(n, context)
+ for n in deepcopy(node.else_))
+ except nodes.Impossible:
+ return node
+ finally:
+ context.pop()
return result
def visit_If(self, node, context):
@@ -127,9 +162,9 @@ def walk(target, value):
value = tuple(value)
except TypeError:
raise nodes.Impossible()
- if len(target) != len(value):
+ if len(target.items) != len(value):
raise nodes.Impossible()
- for name, val in zip(target, value):
+ for name, val in zip(target.items, value):
walk(name, val)
else:
raise AssertionError('unexpected assignable node')
@@ -140,6 +175,17 @@ def walk(target, value):
return node
return result
+ def fold(self, node, context):
+ """Do constant folding."""
+ node = self.generic_visit(node, context)
+ try:
+ return nodes.Const(node.as_const(), lineno=node.lineno)
+ except nodes.Impossible:
+ return node
+ visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
+ visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
+ visit_Not = visit_Compare = fold
+
def visit_Subscript(self, node, context):
if node.ctx == 'load':
try:
View
@@ -14,7 +14,7 @@
defaultdict = None
-__all__ = ['extends', 'subscribe', 'TemplateContext', 'Macro']
+__all__ = ['extends', 'subscribe', 'LoopContext', 'TemplateContext', 'Macro']
def extends(template, namespace):
@@ -74,6 +74,36 @@ def __missing__(self, key):
return self.undefined_factory(key)
+class LoopContext(object):
+ """Helper for extended iteration."""
+
+ def __init__(self, iterable, parent=None):
+ self._iterable = iterable
+ self.index0 = 0
+ self.parent = parent
+
+ def __iter__(self):
+ for item in self._iterable:
+ yield self, item
+ self.index0 += 1
+
+ first = property(lambda x: x.index0 == 0)
+ last = property(lambda x: x.revindex0 == 0)
+ index = property(lambda x: x.index0 + 1)
+ revindex = property(lambda x: x.length)
+ revindex0 = property(lambda x: x.length - 1)
+
+ @property
+ def length(self):
+ if not hasattr(self, '_length'):
+ try:
+ length = len(self._iterable)
+ except TypeError:
+ length = len(tuple(self._iterable))
+ self._length = length
+ return self._length
+
+
class Macro(object):
"""
Wraps a macor
View
@@ -17,10 +17,10 @@
{{ readstatus(forum.id) }} {{ forum.id|e }} {{ forum.name|e }}
{% endfor %}
- {% navigation = [('#foo', 'Foo'), ('#bar', 'Bar')] %}
+ {% navigation = [('#foo', 'Foo'), ('#bar', 'Bar'), ('#baz', 42 * 2 + 23)] %}
<ul>
- {% for item in navigation %}
- <li><a href="{{ item[0] }}">{{ item[1] }}</a></li>
+ {% for key, value in navigation %}
+ <li>{{ loop.index }}: <a href="{{ key|e }}">{{ value|e }}</a></li>
{% endfor %}
</ul>
""")

0 comments on commit bfe7a6b

Please sign in to comment.