From 10f3ba209e1741723b01c8d961ea4f6ef1c97525 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 18 Apr 2008 11:30:37 +0200 Subject: [PATCH] loops and `tests` tests pass now --HG-- branch : trunk --- jinja2/compiler.py | 10 +++++++++- jinja2/parser.py | 4 ++-- jinja2/runtime.py | 7 ++++--- tests/test_forloop.py | 16 ++++++++-------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index b7aae46..871728f 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -76,6 +76,9 @@ class Identifiers(object): # frames or because they are special for the frame) self.declared = set() + # undeclared variables from outer scopes + self.outer_undeclared = set() + # names that are accessed without being explicitly declared by # this one or any of the outer scopes. Names can appear both in # declared and undeclared. @@ -106,7 +109,8 @@ class Identifiers(object): def find_shadowed(self): """Find all the shadowed names.""" - return self.declared & (self.declared_locally | self.declared_parameter) + return (self.declared | self.outer_undeclared) & \ + (self.declared_locally | self.declared_parameter) class Frame(object): @@ -145,6 +149,10 @@ class Frame(object): parent.identifiers.declared_locally | parent.identifiers.declared_parameter ) + self.identifiers.outer_undeclared.update( + parent.identifiers.undeclared - + self.identifiers.declared + ) self.buffer = parent.buffer self.name_overrides = parent.name_overrides.copy() diff --git a/jinja2/parser.py b/jinja2/parser.py index 84a317c..8b82a4d 100644 --- a/jinja2/parser.py +++ b/jinja2/parser.py @@ -626,6 +626,8 @@ class Parser(object): else: negated = False name = self.stream.expect('name').value + dyn_args = dyn_kwargs = None + kwargs = [] if self.stream.current.type is 'lparen': args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None) elif self.stream.current.type in ('name', 'string', 'integer', @@ -634,8 +636,6 @@ class Parser(object): args = [self.parse_expression()] else: args = [] - kwargs = [] - dyn_args = dyn_kwargs = None node = nodes.Test(node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno) if negated: diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 1f1d7bd..e1bf486 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -142,8 +142,8 @@ class LoopContextBase(object): 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) + revindex = property(lambda x: x.length - x.index0) + revindex0 = property(lambda x: x.length - x.index) def __len__(self): return self.length @@ -191,7 +191,8 @@ class StaticLoopContext(LoopContextBase): """The static loop context is used in the optimizer to "freeze" the status of an iteration. The only reason for this object is if the loop object is accessed in a non static way (eg: becomes part of a - function call).""" + function call). + """ def __init__(self, index0, length): self.index0 = index0 diff --git a/tests/test_forloop.py b/tests/test_forloop.py index ed1cc2e..dd25494 100644 --- a/tests/test_forloop.py +++ b/tests/test_forloop.py @@ -6,6 +6,8 @@ :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +from py.test import raises + SIMPLE = '''{% for item in seq %}{{ item }}{% endfor %}''' ELSE = '''{% for item in seq %}XXX{% else %}...{% endfor %}''' @@ -13,9 +15,9 @@ EMPTYBLOCKS = '''<{% for item in seq %}{% else %}{% endfor %}>''' CONTEXTVARS = '''{% for item in seq %}\ {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ - loop.even }}|{{ loop.odd }}|{{ loop.length }}###{% endfor %}''' -CYCLING = '''{% for item in seq %}{% cycle '<1>', '<2>' %}{% endfor %}\ -{% for item in seq %}{% cycle through %}{% endfor %}''' + loop.length }}###{% endfor %}''' +CYCLING = '''{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}\ +{% for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''' SCOPE = '''{% for item in seq %}{% endfor %}{{ item }}''' VARLEN = '''{% for item in iter %}{{ item }}{% endfor %}''' NONITER = '''{% for item in none %}...{% endfor %}''' @@ -40,9 +42,9 @@ def test_context_vars(env): tmpl = env.from_string(CONTEXTVARS) one, two, _ = tmpl.render(seq=[0, 1]).split('###') (one_index, one_index0, one_revindex, one_revindex0, one_first, - one_last, one_even, one_odd, one_length) = one.split('|') + one_last, one_length) = one.split('|') (two_index, two_index0, two_revindex, two_revindex0, two_first, - two_last, two_even, two_odd, two_length) = two.split('|') + two_last, two_length) = two.split('|') assert int(one_index) == 1 and int(two_index) == 2 assert int(one_index0) == 0 and int(two_index0) == 1 @@ -50,8 +52,6 @@ def test_context_vars(env): assert int(one_revindex0) == 1 and int(two_revindex0) == 0 assert one_first == 'True' and two_first == 'False' assert one_last == 'False' and two_last == 'True' - assert one_even == 'False' and two_even == 'True' - assert one_odd == 'True' and two_odd == 'False' assert one_length == two_length == '2' @@ -78,4 +78,4 @@ def test_varlen(env): def test_noniter(env): tmpl = env.from_string(NONITER) - assert not tmpl.render() + raises(TypeError, tmpl.render) -- 2.26.2