- once again improved debugger.
+- added `groupby` filter.
+
Version 1.1
-----------
"""
+#: list of lorem ipsum words used by the lipsum() helper function
LOREM_IPSUM_WORDS = u'''\
a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
auctor augue bibendum blandit class commodo condimentum congue consectetuer
import sys
from random import randrange
-from opcode import opmap
-from types import CodeType
# if we have extended debugger support we should really use it
try:
class TracebackLoader(object):
"""
- Fake importer that just returns the source of a template.
+ Fake importer that just returns the source of a template. It's just used
+ by Jinja interally and you shouldn't use it on your own.
"""
def __init__(self, environment, source, filename):
Jinja template sourcecode. Very hackish indeed.
"""
# check for linecache, not every implementation of python
- # might have such an module.
+ # might have such an module (this check is pretty senseless
+ # because we depend on cpython anway)
try:
from linecache import cache
except ImportError:
import re
from random import choice
from urllib import urlencode, quote
-from jinja.utils import urlize, escape, reversed, sorted
+from jinja.utils import urlize, escape, reversed, sorted, groupby
from jinja.datastructure import TemplateData
from jinja.exceptions import FilterArgumentError
return wrapped
+def do_groupby(attribute):
+ """
+ Group a sequence of objects by a common attribute.
+
+ If you for example have a list of dicts or objects that represent persons
+ with `gender`, `first_name` and `last_name` attributes and you want to
+ group all users by genders you can do something like the following
+ snippet:
+
+ .. sourcecode:: html+jinja
+
+ <ul>
+ {% for group in persons|groupby('gender') %}
+ <li>{{ group.grouper }}<ul>
+ {% for person in group.list %}
+ <li>{{ person.first_name }} {{ person.last_name }}</li>
+ {% endfor %}</ul></li>
+ {% endfor %}
+ </ul>
+
+ As you can see the item we're grouping by is stored in the `grouper`
+ attribute and the `list` contains all the objects that have this grouper
+ in common.
+ """
+ def wrapped(env, context, value):
+ expr = lambda x: env.get_attribute(x, attribute)
+ return [{
+ 'grouper': a,
+ 'list': list(b)
+ } for a, b in groupby(sorted(value, key=expr), expr)]
+ return wrapped
+
+
FILTERS = {
'replace': do_replace,
'upper': do_upper,
'sum': do_sum,
'abs': do_abs,
'round': do_round,
- 'sort': do_sort
+ 'sort': do_sort,
+ 'groupby': do_groupby
}
except ImportError:
has_extended_debugger = False
+# group by support
+try:
+ from itertools import groupby
+except ImportError:
+ class groupby(object):
+
+ def __init__(self, iterable, key=lambda x: x):
+ self.keyfunc = key
+ self.it = iter(iterable)
+ self.tgtkey = self.currkey = self.currvalue = xrange(0)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ while self.currkey == self.tgtkey:
+ self.currvalue = self.it.next()
+ self.currkey = self.keyfunc(self.currvalue)
+ self.tgtkey = self.currkey
+ return (self.currkey, self._grouper(self.tgtkey))
+
+ def _grouper(self, tgtkey):
+ while self.currkey == tgtkey:
+ yield self.currvalue
+ self.currvalue = self.it.next()
+ self.currkey = self.keyfunc(self.currvalue)
+
#: function types
callable_types = (FunctionType, MethodType)
{% include 'syntax_broken' %}
''',
- '/code_runtime_error': u'''
-{{ broken() }}
-''',
+ '/code_runtime_error': u'''We have a runtime error here:
+ {{ broken() }}''',
'runtime_broken': '''\
This is an included template
XMLATTR = '''{{ {'foo': 42, 'bar': 23, 'fish': none,
'spam': missing, 'blub:blub': '<?>'}|xmlattr }}'''
SORT = '''{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}'''
+GROUPBY = '''{{ [{'foo': 1, 'bar': 2},
+ {'foo': 2, 'bar': 3},
+ {'foo': 1, 'bar': 1},
+ {'foo': 3, 'bar': 4}]|groupby('foo') }}'''
def test_sort(env):
tmpl = env.from_string(SORT)
assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+
+def test_groupby(env):
+ tmpl = env.from_string(GROUPBY)
+ assert tmpl.render() == (
+ "[{'list': [{'foo': 1, 'bar': 2}, {'foo': 1, 'bar': 1}], "
+ "'grouper': 1}, {'list': [{'foo': 2, 'bar': 3}], 'grouper': 2}, "
+ "{'list': [{'foo': 3, 'bar': 4}], 'grouper': 3}]"
+ )