From: Armin Ronacher Date: Wed, 28 Dec 2016 22:18:49 +0000 (+0100) Subject: First pass on async support for filters X-Git-Tag: 2.9~57 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6445ec0861f1d10f55003013a318452ba0e77036;p=thirdparty%2Fjinja.git First pass on async support for filters --- diff --git a/jinja2/asyncfilters.py b/jinja2/asyncfilters.py new file mode 100644 index 00000000..58a44a37 --- /dev/null +++ b/jinja2/asyncfilters.py @@ -0,0 +1,34 @@ +from functools import wraps +from jinja2.asyncsupport import auto_aiter +from jinja2 import filters + + +async def auto_to_seq(value): + seq = [] + if hasattr(value, '__aiter__'): + async for item in value: + seq.append(item) + else: + for item in value: + seq.append(item) + return seq + + +async def async_do_first(environment, seq): + try: + return await auto_aiter(seq).__anext__() + except StopAsyncIteration: + return environment.undefined('No first item, sequence was empty.') + + +@wraps(filters.do_first) +@filters.environmentfilter +def do_first(environment, seq): + if environment.is_async: + return async_do_first(environment, seq) + return filters.do_first(environment, seq) + + +ASYNC_FILTERS = { + 'first': do_first, +} diff --git a/jinja2/asyncsupport.py b/jinja2/asyncsupport.py index 1cdda833..33a1a071 100644 --- a/jinja2/asyncsupport.py +++ b/jinja2/asyncsupport.py @@ -139,12 +139,20 @@ def patch_template(): def patch_runtime(): from jinja2.runtime import BlockReference - BlockReference.__call__ = wrap_block_reference_call(BlockReference.__call__) + BlockReference.__call__ = wrap_block_reference_call( + BlockReference.__call__) + + +def patch_filters(): + from jinja2.filters import FILTERS + from jinja2.asyncfilters import ASYNC_FILTERS + FILTERS.update(ASYNC_FILTERS) def patch_all(): patch_template() patch_runtime() + patch_filters() async def auto_await(value): diff --git a/jinja2/compiler.py b/jinja2/compiler.py index c01d5b1a..056d60ea 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -1615,6 +1615,8 @@ class CodeGenerator(NodeVisitor): self.visit(node.step, frame) def visit_Filter(self, node, frame): + if self.environment.is_async: + self.write('await auto_await(') self.write(self.filters[node.name] + '(') func = self.environment.filters.get(node.name) if func is None: @@ -1640,6 +1642,8 @@ class CodeGenerator(NodeVisitor): self.write('concat(%s)' % frame.buffer) self.signature(node, frame) self.write(')') + if self.environment.is_async: + self.write(')') def visit_Test(self, node, frame): self.write(self.tests[node.name] + '(') diff --git a/tests/conftest.py b/tests/conftest.py index f1ae10ff..eaae2b0c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,7 +21,7 @@ from jinja2 import Environment def pytest_ignore_collect(path, config): - if path.basename == 'test_async.py' and not have_async_gen: + if 'async' in path.basename and not have_async_gen: return True return False diff --git a/tests/test_asyncfilters.py b/tests/test_asyncfilters.py new file mode 100644 index 00000000..75ee4a3b --- /dev/null +++ b/tests/test_asyncfilters.py @@ -0,0 +1,22 @@ +import pytest +from jinja2 import Environment + + +@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 + tmpl = env_async.from_string('{{ foo()|first }}') + out = tmpl.render(foo=foo) + assert out == '0' diff --git a/tests/test_filters.py b/tests/test_filters.py index f8fdad8a..59999ee4 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -14,7 +14,7 @@ from jinja2._compat import text_type, implements_to_string @pytest.mark.filter -class TestFilter(): +class TestFilter(object): def test_filter_calling(self, env): rv = env.call_filter('sum', [1, 2, 3])