From add79164c421df81022b0b93ebd021cf9ae64096 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 28 Jan 2017 15:33:09 +0100 Subject: [PATCH] Fixed async macro autoescape. Fixes #671 --- CHANGES | 1 + jinja2/asyncsupport.py | 19 ++++++++++++++++++- jinja2/runtime.py | 4 ++++ tests/test_async.py | 15 +++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cfaf85e9..7f112297 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Version 2.9.5 async environments. (#668) - Reduce memory footprint slightly by reducing our unicode database dump we use for identifier matching on Python 3 (#666) +- Fixed autoescaping not working for macros in async compilation mode. (#671) Version 2.9.4 ------------- diff --git a/jinja2/asyncsupport.py b/jinja2/asyncsupport.py index 6e82d75b..f4f264ad 100644 --- a/jinja2/asyncsupport.py +++ b/jinja2/asyncsupport.py @@ -96,6 +96,22 @@ def wrap_block_reference_call(original_call): return update_wrapper(__call__, original_call) +def wrap_macro_invoke(original_invoke): + @internalcode + async def async_invoke(self, arguments, autoescape): + rv = await self._func(*arguments) + if autoescape: + rv = Markup(rv) + return rv + + @internalcode + def _invoke(self, arguments, autoescape): + if not self._environment.is_async: + return original_invoke(self, arguments, autoescape) + return async_invoke(self, arguments, autoescape) + return update_wrapper(_invoke, original_invoke) + + @internalcode async def get_default_module_async(self): if self._module is not None: @@ -138,9 +154,10 @@ def patch_template(): def patch_runtime(): - from jinja2.runtime import BlockReference + from jinja2.runtime import BlockReference, Macro BlockReference.__call__ = wrap_block_reference_call( BlockReference.__call__) + Macro._invoke = wrap_macro_invoke(Macro._invoke) def patch_filters(): diff --git a/jinja2/runtime.py b/jinja2/runtime.py index f39e89bd..24430016 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -544,6 +544,10 @@ class Macro(object): raise TypeError('macro %r takes not more than %d argument(s)' % (self.name, len(self.arguments))) + return self._invoke(arguments, autoescape) + + def _invoke(self, arguments, autoescape): + """This method is being swapped out by the async implementation.""" rv = self._func(*arguments) if autoescape: rv = Markup(rv) diff --git a/tests/test_async.py b/tests/test_async.py index 8618d8d0..279a4bb3 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -243,6 +243,21 @@ class TestAsyncIncludes(object): """) assert t.render().strip() == '(FOO)' + def test_unoptimized_scopes_autoescape(self): + env = Environment(loader=DictLoader(dict( + o_printer='({{ o }})', + )), autoescape=True, enable_async=True) + t = env.from_string(""" + {% macro outer(o) %} + {% macro inner() %} + {% include "o_printer" %} + {% endmacro %} + {{ inner() }} + {% endmacro %} + {{ outer("FOO") }} + """) + assert t.render().strip() == '(FOO)' + @pytest.mark.core_tags @pytest.mark.for_loop -- 2.47.2