From: Armin Ronacher Date: Wed, 28 Dec 2016 22:39:03 +0000 (+0100) Subject: Made groupby work with async X-Git-Tag: 2.9~54 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c62100321423db25e329e06656c35cf8975a70a;p=thirdparty%2Fjinja.git Made groupby work with async --- diff --git a/jinja2/asyncfilters.py b/jinja2/asyncfilters.py index 31222d0d..e879893c 100644 --- a/jinja2/asyncfilters.py +++ b/jinja2/asyncfilters.py @@ -1,4 +1,5 @@ from functools import wraps + from jinja2.asyncsupport import auto_aiter from jinja2 import filters @@ -53,6 +54,15 @@ async def do_first(environment, seq): return environment.undefined('No first item, sequence was empty.') +@asyncfiltervariant(filters.do_groupby) +async def do_groupby(environment, value, attribute): + expr = filters.make_attrgetter(environment, attribute) + return [filters._GroupTuple(key, await auto_to_seq(values)) + for key, values in filters.groupby(sorted( + await auto_to_seq(value), key=expr), expr)] + + ASYNC_FILTERS = { 'first': do_first, + 'groupby': do_groupby, } diff --git a/tests/test_asyncfilters.py b/tests/test_asyncfilters.py index 75ee4a3b..5133f9cb 100644 --- a/tests/test_asyncfilters.py +++ b/tests/test_asyncfilters.py @@ -2,21 +2,81 @@ import pytest from jinja2 import Environment +async def make_aiter(iter): + for item in iter: + yield item + + @pytest.fixture def env_async(): return Environment(enable_async=True) -def test_first(env_async): - tmpl = env_async.from_string('{{ foo|first }}') - out = tmpl.render(foo=list(range(10))) - assert out == '0' - - -def test_first_aiter(env_async): - async def foo(): - for x in range(10): - yield x +@pytest.mark.parametrize('foo', [ + lambda: range(10), + lambda: make_aiter(range(10)), +]) +def test_first(env_async, foo): tmpl = env_async.from_string('{{ foo()|first }}') out = tmpl.render(foo=foo) assert out == '0' + + +def test_groupby(env_async): + tmpl = env_async.from_string(''' + {%- for grouper, list in [{'foo': 1, 'bar': 2}, + {'foo': 2, 'bar': 3}, + {'foo': 1, 'bar': 1}, + {'foo': 3, 'bar': 4}]|groupby('foo') -%} + {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}| + {%- endfor %}''') + assert tmpl.render().split('|') == [ + "1: 1, 2: 1, 1", + "2: 2, 3", + "3: 3, 4", + "" + ] + + +def test_groupby_tuple_index(env_async): + tmpl = env_async.from_string(''' + {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%} + {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}| + {%- endfor %}''') + assert tmpl.render() == 'a:1:2|b:1|' + + +def make_articles(): + class Date(object): + def __init__(self, day, month, year): + self.day = day + self.month = month + self.year = year + + class Article(object): + def __init__(self, title, *date): + self.date = Date(*date) + self.title = title + + return [ + Article('aha', 1, 1, 1970), + Article('interesting', 2, 1, 1970), + Article('really?', 3, 1, 1970), + Article('totally not', 1, 1, 1971) + ] + + +@pytest.mark.parametrize('articles', [ + make_articles, + lambda: make_aiter(make_articles()), +]) +def test_groupby_multidot(env_async, articles): + tmpl = env_async.from_string(''' + {%- for year, list in articles()|groupby('date.year') -%} + {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}| + {%- endfor %}''') + assert tmpl.render(articles=articles).split('|') == [ + '1970[aha][interesting][really?]', + '1971[totally not]', + '' + ]