Permalink
Browse files

added loopcontrols extension and added unittests for it

--HG--
branch : trunk
  • Loading branch information...
1 parent 105f0dc commit 3da9031b99b12ad829e8307b893279347ad04c2c @mitsuhiko mitsuhiko committed May 23, 2008
Showing with 93 additions and 11 deletions.
  1. +11 −0 docs/extensions.rst
  2. +22 −0 docs/templates.rst
  3. +7 −9 jinja2/compiler.py
  4. +12 −0 jinja2/ext.py
  5. +4 −2 jinja2/nodes.py
  6. +37 −0 tests/test_ext.py
View
11 docs/extensions.rst
@@ -101,6 +101,17 @@ The do aka expression-statement extension adds a simple `do` tag to the
template engine that works like a variable expression but ignores the
return value.
+.. _loopcontrols-extension:
+
+loopcontrols
+~~~~~~~~~~~~
+
+**Import name:** `jinja2.ext.loopcontrols`
+
+This extension adds support for `break` and `continue` in loops. After
+enabling Jinja2 provides those two keywords which work exactly like in
+Python.
+
.. _writing-extensions:
View
22 docs/templates.rst
@@ -1025,3 +1025,25 @@ that works exactly like the regular variable expression (``{{ ... }}``) just
that it doesn't print anything. This can be used to modify lists::
{% do navigation.append('a string') %}
+
+
+Loop Controls
+~~~~~~~~~~~~~
+
+If the application enables the :ref:`loopcontrols-extension` it's possible to
+use `break` and `continue` in loops. When `break` is reached, the loop is
+terminated, if `continue` is eached the processing is stopped and continues
+with the next iteration.
+
+Here a loop that skips every second item::
+
+ {% for user in users %}
+ {%- if loop.index is even %}{% continue %}{% endif %}
+ ...
+ {% endfor %}
+
+Likewise a look that stops processing after the 10th iteration::
+
+ {% for user in users %}
+ {%- if loop.index >= 10 %}{% break %}{% endif %}
+ {%- endfor %}
View
16 jinja2/compiler.py
@@ -1018,8 +1018,8 @@ def visit_Macro(self, node, frame):
self.macro_def(node, macro_frame)
def visit_CallBlock(self, node, frame):
- call_frame = self.macro_body(node, frame, node.iter_child_nodes
- (exclude=('call',)))
+ children = node.iter_child_nodes(exclude=('call',))
+ call_frame = self.macro_body(node, frame, children)
self.writeline('caller = ')
self.macro_def(node, call_frame)
self.start_write(frame, node)
@@ -1305,14 +1305,12 @@ def visit_Filter(self, node, frame):
# if the filter node is None we are inside a filter block
# and want to write to the current buffer
- if node.node is None:
- if self.environment.autoescape:
- tmpl = 'Markup(concat(%s))'
- else:
- tmpl = 'concat(%s)'
- self.write(tmpl % frame.buffer)
- else:
+ if node.node is not None:
self.visit(node.node, frame)
+ elif self.environment.autoescape:
+ self.write('Markup(concat(%s))' % frame.buffer)
+ else:
+ self.write('concat(%s)' % frame.buffer)
self.signature(node, frame)
self.write(')')
View
12 jinja2/ext.py
@@ -283,6 +283,17 @@ def parse(self, parser):
return node
+class LoopControlExtension(Extension):
+ """Adds break and continue to the template engine."""
+ tags = set(['break', 'continue'])
+
+ def parse(self, parser):
+ token = parser.stream.next()
+ if token.value == 'break':
+ return nodes.Break(lineno=token.lineno)
+ return nodes.Continue(lineno=token.lineno)
+
+
def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS):
"""Extract localizable strings from the given template node.
@@ -367,3 +378,4 @@ def babel_extract(fileobj, keywords, comment_tags, options):
#: nicer import names
i18n = InternationalizationExtension
do = ExprStmtExtension
+loopcontrols = LoopControlExtension
View
6 jinja2/nodes.py
@@ -118,8 +118,10 @@ def __init__(self, *fields, **attributes):
def iter_fields(self, exclude=None, only=None):
"""This method iterates over all fields that are defined and yields
- ``(key, value)`` tuples. Optionally a parameter of ignored fields
- can be provided.
+ ``(key, value)`` tuples. Per default all fields are returned, but
+ it's possible to limit that to some fields by providing the `only`
+ parameter or to exclude some using the `exclude` parameter. Both
+ should be sets or tuples of field names.
"""
for name in self.fields:
if (exclude is only is None) or \
View
37 tests/test_ext.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+"""
+ unit test for some extensions
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2008 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2 import Environment
+
+
+def test_loop_controls():
+ env = Environment(extensions=['jinja2.ext.loopcontrols'])
+
+ tmpl = env.from_string('''
+ {%- for item in [1, 2, 3, 4] %}
+ {%- if item % 2 == 0 %}{% continue %}{% endif -%}
+ {{ item }}
+ {%- endfor %}''')
+ assert tmpl.render() == '13'
+
+ tmpl = env.from_string('''
+ {%- for item in [1, 2, 3, 4] %}
+ {%- if item > 2 %}{% break %}{% endif -%}
+ {{ item }}
+ {%- endfor %}''')
+ assert tmpl.render() == '12'
+
+
+def test_do():
+ env = Environment(extensions=['jinja2.ext.do'])
+ tmpl = env.from_string('''
+ {%- set items = [] %}
+ {%- for char in "foo" %}
+ {%- do items.append(loop.index0 ~ char) %}
+ {%- endfor %}{{ items|join(', ') }}''')
+ assert tmpl.render() == '0f, 1o, 2o'

0 comments on commit 3da9031

Please sign in to comment.