Viewing file: asyncsupport.py (7.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- coding: utf-8 -*- """The code for async support. Importing this patches Jinja on supported Python versions. """ import asyncio import inspect from functools import update_wrapper
from markupsafe import Markup
from .environment import TemplateModule from .runtime import LoopContext from .utils import concat from .utils import internalcode from .utils import missing
async def concat_async(async_gen): rv = []
async def collect(): async for event in async_gen: rv.append(event)
await collect() return concat(rv)
async def generate_async(self, *args, **kwargs): vars = dict(*args, **kwargs) try: async for event in self.root_render_func(self.new_context(vars)): yield event except Exception: yield self.environment.handle_exception()
def wrap_generate_func(original_generate): def _convert_generator(self, loop, args, kwargs): async_gen = self.generate_async(*args, **kwargs) try: while 1: yield loop.run_until_complete(async_gen.__anext__()) except StopAsyncIteration: pass
def generate(self, *args, **kwargs): if not self.environment.is_async: return original_generate(self, *args, **kwargs) return _convert_generator(self, asyncio.get_event_loop(), args, kwargs)
return update_wrapper(generate, original_generate)
async def render_async(self, *args, **kwargs): if not self.environment.is_async: raise RuntimeError("The environment was not created with async mode enabled.")
vars = dict(*args, **kwargs) ctx = self.new_context(vars)
try: return await concat_async(self.root_render_func(ctx)) except Exception: return self.environment.handle_exception()
def wrap_render_func(original_render): def render(self, *args, **kwargs): if not self.environment.is_async: return original_render(self, *args, **kwargs) loop = asyncio.get_event_loop() return loop.run_until_complete(self.render_async(*args, **kwargs))
return update_wrapper(render, original_render)
def wrap_block_reference_call(original_call): @internalcode async def async_call(self): rv = await concat_async(self._stack[self._depth](self._context)) if self._context.eval_ctx.autoescape: rv = Markup(rv) return rv
@internalcode def __call__(self): if not self._context.environment.is_async: return original_call(self) return async_call(self)
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: return self._module self._module = rv = await self.make_module_async() return rv
def wrap_default_module(original_default_module): @internalcode def _get_default_module(self): if self.environment.is_async: raise RuntimeError("Template module attribute is unavailable in async mode") return original_default_module(self)
return _get_default_module
async def make_module_async(self, vars=None, shared=False, locals=None): context = self.new_context(vars, shared, locals) body_stream = [] async for item in self.root_render_func(context): body_stream.append(item) return TemplateModule(self, context, body_stream)
def patch_template(): from . import Template
Template.generate = wrap_generate_func(Template.generate) Template.generate_async = update_wrapper(generate_async, Template.generate_async) Template.render_async = update_wrapper(render_async, Template.render_async) Template.render = wrap_render_func(Template.render) Template._get_default_module = wrap_default_module(Template._get_default_module) Template._get_default_module_async = get_default_module_async Template.make_module_async = update_wrapper( make_module_async, Template.make_module_async )
def patch_runtime(): from .runtime import BlockReference, Macro
BlockReference.__call__ = wrap_block_reference_call(BlockReference.__call__) Macro._invoke = wrap_macro_invoke(Macro._invoke)
def patch_filters(): from .filters import FILTERS from .asyncfilters import ASYNC_FILTERS
FILTERS.update(ASYNC_FILTERS)
def patch_all(): patch_template() patch_runtime() patch_filters()
async def auto_await(value): if inspect.isawaitable(value): return await value return value
async def auto_aiter(iterable): if hasattr(iterable, "__aiter__"): async for item in iterable: yield item return for item in iterable: yield item
class AsyncLoopContext(LoopContext): _to_iterator = staticmethod(auto_aiter)
@property async def length(self): if self._length is not None: return self._length
try: self._length = len(self._iterable) except TypeError: iterable = [x async for x in self._iterator] self._iterator = self._to_iterator(iterable) self._length = len(iterable) + self.index + (self._after is not missing)
return self._length
@property async def revindex0(self): return await self.length - self.index
@property async def revindex(self): return await self.length - self.index0
async def _peek_next(self): if self._after is not missing: return self._after
try: self._after = await self._iterator.__anext__() except StopAsyncIteration: self._after = missing
return self._after
@property async def last(self): return await self._peek_next() is missing
@property async def nextitem(self): rv = await self._peek_next()
if rv is missing: return self._undefined("there is no next item")
return rv
def __aiter__(self): return self
async def __anext__(self): if self._after is not missing: rv = self._after self._after = missing else: rv = await self._iterator.__anext__()
self.index0 += 1 self._before = self._current self._current = rv return rv, self
async def make_async_loop_context(iterable, undefined, recurse=None, depth0=0): import warnings
warnings.warn( "This template must be recompiled with at least Jinja 2.11, or" " it will fail in 3.0.", DeprecationWarning, stacklevel=2, ) return AsyncLoopContext(iterable, undefined, recurse, depth0)
patch_all()
|