11cb0ef41Sopenharmony_ci# -*- coding: utf-8 -*- 21cb0ef41Sopenharmony_ci""" 31cb0ef41Sopenharmony_ci jinja2.asyncsupport 41cb0ef41Sopenharmony_ci ~~~~~~~~~~~~~~~~~~~ 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci Has all the code for async support which is implemented as a patch 71cb0ef41Sopenharmony_ci for supported Python versions. 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci :copyright: (c) 2017 by the Jinja Team. 101cb0ef41Sopenharmony_ci :license: BSD, see LICENSE for more details. 111cb0ef41Sopenharmony_ci""" 121cb0ef41Sopenharmony_ciimport sys 131cb0ef41Sopenharmony_ciimport asyncio 141cb0ef41Sopenharmony_ciimport inspect 151cb0ef41Sopenharmony_cifrom functools import update_wrapper 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_cifrom jinja2.utils import concat, internalcode, Markup 181cb0ef41Sopenharmony_cifrom jinja2.environment import TemplateModule 191cb0ef41Sopenharmony_cifrom jinja2.runtime import LoopContextBase, _last_iteration 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciasync def concat_async(async_gen): 231cb0ef41Sopenharmony_ci rv = [] 241cb0ef41Sopenharmony_ci async def collect(): 251cb0ef41Sopenharmony_ci async for event in async_gen: 261cb0ef41Sopenharmony_ci rv.append(event) 271cb0ef41Sopenharmony_ci await collect() 281cb0ef41Sopenharmony_ci return concat(rv) 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci 311cb0ef41Sopenharmony_ciasync def generate_async(self, *args, **kwargs): 321cb0ef41Sopenharmony_ci vars = dict(*args, **kwargs) 331cb0ef41Sopenharmony_ci try: 341cb0ef41Sopenharmony_ci async for event in self.root_render_func(self.new_context(vars)): 351cb0ef41Sopenharmony_ci yield event 361cb0ef41Sopenharmony_ci except Exception: 371cb0ef41Sopenharmony_ci exc_info = sys.exc_info() 381cb0ef41Sopenharmony_ci else: 391cb0ef41Sopenharmony_ci return 401cb0ef41Sopenharmony_ci yield self.environment.handle_exception(exc_info, True) 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_cidef wrap_generate_func(original_generate): 441cb0ef41Sopenharmony_ci def _convert_generator(self, loop, args, kwargs): 451cb0ef41Sopenharmony_ci async_gen = self.generate_async(*args, **kwargs) 461cb0ef41Sopenharmony_ci try: 471cb0ef41Sopenharmony_ci while 1: 481cb0ef41Sopenharmony_ci yield loop.run_until_complete(async_gen.__anext__()) 491cb0ef41Sopenharmony_ci except StopAsyncIteration: 501cb0ef41Sopenharmony_ci pass 511cb0ef41Sopenharmony_ci def generate(self, *args, **kwargs): 521cb0ef41Sopenharmony_ci if not self.environment.is_async: 531cb0ef41Sopenharmony_ci return original_generate(self, *args, **kwargs) 541cb0ef41Sopenharmony_ci return _convert_generator(self, asyncio.get_event_loop(), args, kwargs) 551cb0ef41Sopenharmony_ci return update_wrapper(generate, original_generate) 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ciasync def render_async(self, *args, **kwargs): 591cb0ef41Sopenharmony_ci if not self.environment.is_async: 601cb0ef41Sopenharmony_ci raise RuntimeError('The environment was not created with async mode ' 611cb0ef41Sopenharmony_ci 'enabled.') 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci vars = dict(*args, **kwargs) 641cb0ef41Sopenharmony_ci ctx = self.new_context(vars) 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci try: 671cb0ef41Sopenharmony_ci return await concat_async(self.root_render_func(ctx)) 681cb0ef41Sopenharmony_ci except Exception: 691cb0ef41Sopenharmony_ci exc_info = sys.exc_info() 701cb0ef41Sopenharmony_ci return self.environment.handle_exception(exc_info, True) 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_cidef wrap_render_func(original_render): 741cb0ef41Sopenharmony_ci def render(self, *args, **kwargs): 751cb0ef41Sopenharmony_ci if not self.environment.is_async: 761cb0ef41Sopenharmony_ci return original_render(self, *args, **kwargs) 771cb0ef41Sopenharmony_ci loop = asyncio.get_event_loop() 781cb0ef41Sopenharmony_ci return loop.run_until_complete(self.render_async(*args, **kwargs)) 791cb0ef41Sopenharmony_ci return update_wrapper(render, original_render) 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_cidef wrap_block_reference_call(original_call): 831cb0ef41Sopenharmony_ci @internalcode 841cb0ef41Sopenharmony_ci async def async_call(self): 851cb0ef41Sopenharmony_ci rv = await concat_async(self._stack[self._depth](self._context)) 861cb0ef41Sopenharmony_ci if self._context.eval_ctx.autoescape: 871cb0ef41Sopenharmony_ci rv = Markup(rv) 881cb0ef41Sopenharmony_ci return rv 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci @internalcode 911cb0ef41Sopenharmony_ci def __call__(self): 921cb0ef41Sopenharmony_ci if not self._context.environment.is_async: 931cb0ef41Sopenharmony_ci return original_call(self) 941cb0ef41Sopenharmony_ci return async_call(self) 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci return update_wrapper(__call__, original_call) 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_cidef wrap_macro_invoke(original_invoke): 1001cb0ef41Sopenharmony_ci @internalcode 1011cb0ef41Sopenharmony_ci async def async_invoke(self, arguments, autoescape): 1021cb0ef41Sopenharmony_ci rv = await self._func(*arguments) 1031cb0ef41Sopenharmony_ci if autoescape: 1041cb0ef41Sopenharmony_ci rv = Markup(rv) 1051cb0ef41Sopenharmony_ci return rv 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci @internalcode 1081cb0ef41Sopenharmony_ci def _invoke(self, arguments, autoescape): 1091cb0ef41Sopenharmony_ci if not self._environment.is_async: 1101cb0ef41Sopenharmony_ci return original_invoke(self, arguments, autoescape) 1111cb0ef41Sopenharmony_ci return async_invoke(self, arguments, autoescape) 1121cb0ef41Sopenharmony_ci return update_wrapper(_invoke, original_invoke) 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci@internalcode 1161cb0ef41Sopenharmony_ciasync def get_default_module_async(self): 1171cb0ef41Sopenharmony_ci if self._module is not None: 1181cb0ef41Sopenharmony_ci return self._module 1191cb0ef41Sopenharmony_ci self._module = rv = await self.make_module_async() 1201cb0ef41Sopenharmony_ci return rv 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_cidef wrap_default_module(original_default_module): 1241cb0ef41Sopenharmony_ci @internalcode 1251cb0ef41Sopenharmony_ci def _get_default_module(self): 1261cb0ef41Sopenharmony_ci if self.environment.is_async: 1271cb0ef41Sopenharmony_ci raise RuntimeError('Template module attribute is unavailable ' 1281cb0ef41Sopenharmony_ci 'in async mode') 1291cb0ef41Sopenharmony_ci return original_default_module(self) 1301cb0ef41Sopenharmony_ci return _get_default_module 1311cb0ef41Sopenharmony_ci 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ciasync def make_module_async(self, vars=None, shared=False, locals=None): 1341cb0ef41Sopenharmony_ci context = self.new_context(vars, shared, locals) 1351cb0ef41Sopenharmony_ci body_stream = [] 1361cb0ef41Sopenharmony_ci async for item in self.root_render_func(context): 1371cb0ef41Sopenharmony_ci body_stream.append(item) 1381cb0ef41Sopenharmony_ci return TemplateModule(self, context, body_stream) 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_cidef patch_template(): 1421cb0ef41Sopenharmony_ci from jinja2 import Template 1431cb0ef41Sopenharmony_ci Template.generate = wrap_generate_func(Template.generate) 1441cb0ef41Sopenharmony_ci Template.generate_async = update_wrapper( 1451cb0ef41Sopenharmony_ci generate_async, Template.generate_async) 1461cb0ef41Sopenharmony_ci Template.render_async = update_wrapper( 1471cb0ef41Sopenharmony_ci render_async, Template.render_async) 1481cb0ef41Sopenharmony_ci Template.render = wrap_render_func(Template.render) 1491cb0ef41Sopenharmony_ci Template._get_default_module = wrap_default_module( 1501cb0ef41Sopenharmony_ci Template._get_default_module) 1511cb0ef41Sopenharmony_ci Template._get_default_module_async = get_default_module_async 1521cb0ef41Sopenharmony_ci Template.make_module_async = update_wrapper( 1531cb0ef41Sopenharmony_ci make_module_async, Template.make_module_async) 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_cidef patch_runtime(): 1571cb0ef41Sopenharmony_ci from jinja2.runtime import BlockReference, Macro 1581cb0ef41Sopenharmony_ci BlockReference.__call__ = wrap_block_reference_call( 1591cb0ef41Sopenharmony_ci BlockReference.__call__) 1601cb0ef41Sopenharmony_ci Macro._invoke = wrap_macro_invoke(Macro._invoke) 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_cidef patch_filters(): 1641cb0ef41Sopenharmony_ci from jinja2.filters import FILTERS 1651cb0ef41Sopenharmony_ci from jinja2.asyncfilters import ASYNC_FILTERS 1661cb0ef41Sopenharmony_ci FILTERS.update(ASYNC_FILTERS) 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_cidef patch_all(): 1701cb0ef41Sopenharmony_ci patch_template() 1711cb0ef41Sopenharmony_ci patch_runtime() 1721cb0ef41Sopenharmony_ci patch_filters() 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ciasync def auto_await(value): 1761cb0ef41Sopenharmony_ci if inspect.isawaitable(value): 1771cb0ef41Sopenharmony_ci return await value 1781cb0ef41Sopenharmony_ci return value 1791cb0ef41Sopenharmony_ci 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ciasync def auto_aiter(iterable): 1821cb0ef41Sopenharmony_ci if hasattr(iterable, '__aiter__'): 1831cb0ef41Sopenharmony_ci async for item in iterable: 1841cb0ef41Sopenharmony_ci yield item 1851cb0ef41Sopenharmony_ci return 1861cb0ef41Sopenharmony_ci for item in iterable: 1871cb0ef41Sopenharmony_ci yield item 1881cb0ef41Sopenharmony_ci 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ciclass AsyncLoopContext(LoopContextBase): 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci def __init__(self, async_iterator, undefined, after, length, recurse=None, 1931cb0ef41Sopenharmony_ci depth0=0): 1941cb0ef41Sopenharmony_ci LoopContextBase.__init__(self, undefined, recurse, depth0) 1951cb0ef41Sopenharmony_ci self._async_iterator = async_iterator 1961cb0ef41Sopenharmony_ci self._after = after 1971cb0ef41Sopenharmony_ci self._length = length 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci @property 2001cb0ef41Sopenharmony_ci def length(self): 2011cb0ef41Sopenharmony_ci if self._length is None: 2021cb0ef41Sopenharmony_ci raise TypeError('Loop length for some iterators cannot be ' 2031cb0ef41Sopenharmony_ci 'lazily calculated in async mode') 2041cb0ef41Sopenharmony_ci return self._length 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ci def __aiter__(self): 2071cb0ef41Sopenharmony_ci return AsyncLoopContextIterator(self) 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ciclass AsyncLoopContextIterator(object): 2111cb0ef41Sopenharmony_ci __slots__ = ('context',) 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci def __init__(self, context): 2141cb0ef41Sopenharmony_ci self.context = context 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci def __aiter__(self): 2171cb0ef41Sopenharmony_ci return self 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci async def __anext__(self): 2201cb0ef41Sopenharmony_ci ctx = self.context 2211cb0ef41Sopenharmony_ci ctx.index0 += 1 2221cb0ef41Sopenharmony_ci if ctx._after is _last_iteration: 2231cb0ef41Sopenharmony_ci raise StopAsyncIteration() 2241cb0ef41Sopenharmony_ci ctx._before = ctx._current 2251cb0ef41Sopenharmony_ci ctx._current = ctx._after 2261cb0ef41Sopenharmony_ci try: 2271cb0ef41Sopenharmony_ci ctx._after = await ctx._async_iterator.__anext__() 2281cb0ef41Sopenharmony_ci except StopAsyncIteration: 2291cb0ef41Sopenharmony_ci ctx._after = _last_iteration 2301cb0ef41Sopenharmony_ci return ctx._current, ctx 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci 2331cb0ef41Sopenharmony_ciasync def make_async_loop_context(iterable, undefined, recurse=None, depth0=0): 2341cb0ef41Sopenharmony_ci # Length is more complicated and less efficient in async mode. The 2351cb0ef41Sopenharmony_ci # reason for this is that we cannot know if length will be used 2361cb0ef41Sopenharmony_ci # upfront but because length is a property we cannot lazily execute it 2371cb0ef41Sopenharmony_ci # later. This means that we need to buffer it up and measure :( 2381cb0ef41Sopenharmony_ci # 2391cb0ef41Sopenharmony_ci # We however only do this for actual iterators, not for async 2401cb0ef41Sopenharmony_ci # iterators as blocking here does not seem like the best idea in the 2411cb0ef41Sopenharmony_ci # world. 2421cb0ef41Sopenharmony_ci try: 2431cb0ef41Sopenharmony_ci length = len(iterable) 2441cb0ef41Sopenharmony_ci except (TypeError, AttributeError): 2451cb0ef41Sopenharmony_ci if not hasattr(iterable, '__aiter__'): 2461cb0ef41Sopenharmony_ci iterable = tuple(iterable) 2471cb0ef41Sopenharmony_ci length = len(iterable) 2481cb0ef41Sopenharmony_ci else: 2491cb0ef41Sopenharmony_ci length = None 2501cb0ef41Sopenharmony_ci async_iterator = auto_aiter(iterable) 2511cb0ef41Sopenharmony_ci try: 2521cb0ef41Sopenharmony_ci after = await async_iterator.__anext__() 2531cb0ef41Sopenharmony_ci except StopAsyncIteration: 2541cb0ef41Sopenharmony_ci after = _last_iteration 2551cb0ef41Sopenharmony_ci return AsyncLoopContext(async_iterator, undefined, after, length, recurse, 2561cb0ef41Sopenharmony_ci depth0) 257