Skip to content

Commit

Permalink
merged
Browse files Browse the repository at this point in the history
--HG--
branch : trunk
  • Loading branch information
tux21b committed Apr 9, 2008
2 parents 2751d94 + 180a1bd commit bfe7a6b
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 17 deletions.
4 changes: 3 additions & 1 deletion jinja2/compiler.py
Expand Up @@ -315,8 +315,10 @@ def visit_For(self, node, frame):
self.writeline('l_loop = None') self.writeline('l_loop = None')
self.write('for ') self.write('for ')
self.visit(node.target, loop_frame) 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) self.visit(node.iter, loop_frame)
if 'loop' in aliases:
self.write(', ' + aliases['loop'])
self.write(extended_loop and '):' or ':') self.write(extended_loop and '):' or ':')
self.blockvisit(node.body, loop_frame) self.blockvisit(node.body, loop_frame)


Expand Down
70 changes: 58 additions & 12 deletions jinja2/optimizer.py
Expand Up @@ -22,7 +22,7 @@
from copy import deepcopy from copy import deepcopy
from jinja2 import nodes from jinja2 import nodes
from jinja2.visitor import NodeVisitor, NodeTransformer from jinja2.visitor import NodeVisitor, NodeTransformer
from jinja2.runtime import subscribe from jinja2.runtime import subscribe, LoopContext




class ContextStack(object): class ContextStack(object):
Expand All @@ -39,6 +39,12 @@ def push(self):
def pop(self): def pop(self):
self.stack.pop() self.stack.pop()


def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default

def __getitem__(self, key): def __getitem__(self, key):
for level in reversed(self.stack): for level in reversed(self.stack):
if key in level: if key in level:
Expand All @@ -58,6 +64,9 @@ class Optimizer(NodeTransformer):
def __init__(self, environment): def __init__(self, environment):
self.environment = environment self.environment = environment


def visit_Block(self, node, context):
return self.generic_visit(node, context.blank())

def visit_Filter(self, node, context): def visit_Filter(self, node, context):
"""Try to evaluate filters if possible.""" """Try to evaluate filters if possible."""
# XXX: nonconstant arguments? not-called visitors? generic visit! # XXX: nonconstant arguments? not-called visitors? generic visit!
Expand All @@ -77,18 +86,44 @@ def visit_For(self, node, context):
iterable = iter(self.visit(node.iter, context).as_const()) iterable = iter(self.visit(node.iter, context).as_const())
except (nodes.Impossible, TypeError): except (nodes.Impossible, TypeError):
return self.generic_visit(node, context) return self.generic_visit(node, context)

parent = context.get('loop')
context.push() context.push()
result = [] result = []
# XXX: tuple unpacking (for key, value in foo)
target = node.target.name
iterated = False iterated = False
for item in iterable:
context[target] = item def assign(target, value):
result.extend(self.visit(n, context) for n in deepcopy(node.body)) if isinstance(target, nodes.Name):
iterated = True context[target.name] = value
if not iterated and node.else_: elif isinstance(target, nodes.Tuple):
result.extend(self.visit(n, context) for n in deepcopy(node.else_)) try:
context.pop() 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 return result


def visit_If(self, node, context): def visit_If(self, node, context):
Expand Down Expand Up @@ -127,9 +162,9 @@ def walk(target, value):
value = tuple(value) value = tuple(value)
except TypeError: except TypeError:
raise nodes.Impossible() raise nodes.Impossible()
if len(target) != len(value): if len(target.items) != len(value):
raise nodes.Impossible() raise nodes.Impossible()
for name, val in zip(target, value): for name, val in zip(target.items, value):
walk(name, val) walk(name, val)
else: else:
raise AssertionError('unexpected assignable node') raise AssertionError('unexpected assignable node')
Expand All @@ -140,6 +175,17 @@ def walk(target, value):
return node return node
return result 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): def visit_Subscript(self, node, context):
if node.ctx == 'load': if node.ctx == 'load':
try: try:
Expand Down
32 changes: 31 additions & 1 deletion jinja2/runtime.py
Expand Up @@ -14,7 +14,7 @@
defaultdict = None defaultdict = None




__all__ = ['extends', 'subscribe', 'TemplateContext', 'Macro'] __all__ = ['extends', 'subscribe', 'LoopContext', 'TemplateContext', 'Macro']




def extends(template, namespace): def extends(template, namespace):
Expand Down Expand Up @@ -74,6 +74,36 @@ def __missing__(self, key):
return self.undefined_factory(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): class Macro(object):
""" """
Wraps a macor Wraps a macor
Expand Down
6 changes: 3 additions & 3 deletions test_optimizer.py
Expand Up @@ -17,10 +17,10 @@
{{ readstatus(forum.id) }} {{ forum.id|e }} {{ forum.name|e }} {{ readstatus(forum.id) }} {{ forum.id|e }} {{ forum.name|e }}
{% endfor %} {% endfor %}
{% navigation = [('#foo', 'Foo'), ('#bar', 'Bar')] %} {% navigation = [('#foo', 'Foo'), ('#bar', 'Bar'), ('#baz', 42 * 2 + 23)] %}
<ul> <ul>
{% for item in navigation %} {% for key, value in navigation %}
<li><a href="{{ item[0] }}">{{ item[1] }}</a></li> <li>{{ loop.index }}: <a href="{{ key|e }}">{{ value|e }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
""") """)
Expand Down

0 comments on commit bfe7a6b

Please sign in to comment.