'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
'evalcontextfilter', 'evalcontextfunction', 'make_logging_undefined',
]
+
+
+def _patch_async():
+ from jinja2.utils import have_async_gen
+ if have_async_gen:
+ from jinja2.asyncsupport import patch_all
+ patch_all()
+
+
+_patch_async()
+del _patch_async
--- /dev/null
+import sys
+import asyncio
+
+from jinja2.utils import concat
+
+
+async def render_async(self, *args, **kwargs):
+ if not self.environment._async:
+ raise RuntimeError('The environment was not created with async mode '
+ 'enabled.')
+
+ vars = dict(*args, **kwargs)
+ ctx = self.new_context(vars)
+ rv = []
+ async def collect():
+ async for event in self.root_render_func(ctx):
+ rv.append(event)
+
+ try:
+ await collect()
+ return concat(rv)
+ except Exception:
+ exc_info = sys.exc_info()
+ return self.environment.handle_exception(exc_info, True)
+
+
+def wrap_render_func(original_render):
+ def render(self, *args, **kwargs):
+ if not self.environment._async:
+ return original_render(self, *args, **kwargs)
+ loop = asyncio.get_event_loop()
+ return loop.run_until_complete(self.render_async(self, *args, **kwargs))
+ return render
+
+
+def patch_template():
+ from jinja2 import Template
+ Template.render_async = render_async
+ Template.render = wrap_render_func(Template.render)
+
+
+def patch_all():
+ patch_template()
except SyntaxError:
pass
-# does this python version support async for in and async generators?
-try:
- exec('async def _():\n async for _ in ():\n yield _')
- have_async_gen = True
-except SyntaxError:
- have_async_gen = False
-
# does if 0: dummy(x) get us x into the scope?
def unoptimize_before_dead_code():
# a = 42; b = lambda: a; del a
self.writeline(' = '.join(to_delete) + ' = missing')
+ def func(self, name):
+ if self.environment._async:
+ return 'async def %s' % name
+ return 'def %s' % name
+
def function_scoping(self, node, frame, children=None,
find_special=True):
"""In Jinja a few statements require the help of anonymous
# and assigned.
if 'loop' in frame.identifiers.declared:
args = args + ['l_loop=l_loop']
- self.writeline('def macro(%s):' % ', '.join(args), node)
+ self.writeline('%s(%s):' % (self.func('macro'), ', '.join(args)), node)
self.indent()
self.buffer(frame)
self.pull_locals(frame)
self.writeline('name = %r' % self.name)
# generate the root render function.
- self.writeline('def root(context%s):' % envenv, extra=1)
+ self.writeline('%s(context%s):' % (self.func('root'), envenv), extra=1)
# process the root
frame = Frame(eval_ctx)
block_frame = Frame(eval_ctx)
block_frame.inspect(block.body)
block_frame.block = name
- self.writeline('def block_%s(context%s):' % (name, envenv),
+ self.writeline('%s(context%s):' % (self.func('block_' + name), envenv),
block, 1)
self.indent()
undeclared = find_undeclared(block.body, ('self', 'super'))
# otherwise we set up a buffer and add a function def
else:
- self.writeline('def loop(reciter, loop_render_func, depth=0):', node)
+ self.writeline('%s(reciter, loop_render_func, depth=0):' %
+ self.func('loop'), node)
self.indent()
self.buffer(loop_frame)
aliases = {}
from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
TemplatesNotFound, TemplateRuntimeError
from jinja2.utils import import_string, LRUCache, Markup, missing, \
- concat, consume, internalcode
+ concat, consume, internalcode, have_async_gen
from jinja2._compat import imap, ifilter, string_types, iteritems, \
text_type, reraise, implements_iterator, implements_to_string, \
encode_filename, PY2, PYPY
self.extensions = load_extensions(self, extensions)
self.enable_async = enable_async
+ self._async = self.enable_async and have_async_gen
_environment_sanity_check(self)
return self.sep
+# does this python version support async for in and async generators?
+try:
+ exec('async def _():\n async for _ in ():\n yield _')
+ have_async_gen = True
+except SyntaxError:
+ have_async_gen = False
+
+
# Imported here because that's where it was in the past
from markupsafe import Markup, escape, soft_unicode
--- /dev/null
+import pytest
+import asyncio
+
+from jinja2 import Template
+from jinja2.utils import have_async_gen
+
+
+def run(func):
+ loop = asyncio.get_event_loop()
+ return loop.run_until_complete(func())
+
+
+@pytest.mark.skipif(not have_async_gen, reason='No async generators')
+def test_basic_async():
+ t = Template('{% for item in [1, 2, 3] %}[{{ item }}]{% endfor %}',
+ enable_async=True)
+ async def func():
+ return await t.render_async()
+
+ rv = run(func)
+ assert rv == '[1][2][3]'