xref: /aosp_15_r20/external/libchrome/third_party/jinja2/asyncsupport.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*635a8641SAndroid Build Coastguard Worker"""
3*635a8641SAndroid Build Coastguard Worker    jinja2.asyncsupport
4*635a8641SAndroid Build Coastguard Worker    ~~~~~~~~~~~~~~~~~~~
5*635a8641SAndroid Build Coastguard Worker
6*635a8641SAndroid Build Coastguard Worker    Has all the code for async support which is implemented as a patch
7*635a8641SAndroid Build Coastguard Worker    for supported Python versions.
8*635a8641SAndroid Build Coastguard Worker
9*635a8641SAndroid Build Coastguard Worker    :copyright: (c) 2017 by the Jinja Team.
10*635a8641SAndroid Build Coastguard Worker    :license: BSD, see LICENSE for more details.
11*635a8641SAndroid Build Coastguard Worker"""
12*635a8641SAndroid Build Coastguard Workerimport sys
13*635a8641SAndroid Build Coastguard Workerimport asyncio
14*635a8641SAndroid Build Coastguard Workerimport inspect
15*635a8641SAndroid Build Coastguard Workerfrom functools import update_wrapper
16*635a8641SAndroid Build Coastguard Worker
17*635a8641SAndroid Build Coastguard Workerfrom jinja2.utils import concat, internalcode, Markup
18*635a8641SAndroid Build Coastguard Workerfrom jinja2.environment import TemplateModule
19*635a8641SAndroid Build Coastguard Workerfrom jinja2.runtime import LoopContextBase, _last_iteration
20*635a8641SAndroid Build Coastguard Worker
21*635a8641SAndroid Build Coastguard Worker
22*635a8641SAndroid Build Coastguard Workerasync def concat_async(async_gen):
23*635a8641SAndroid Build Coastguard Worker    rv = []
24*635a8641SAndroid Build Coastguard Worker    async def collect():
25*635a8641SAndroid Build Coastguard Worker        async for event in async_gen:
26*635a8641SAndroid Build Coastguard Worker            rv.append(event)
27*635a8641SAndroid Build Coastguard Worker    await collect()
28*635a8641SAndroid Build Coastguard Worker    return concat(rv)
29*635a8641SAndroid Build Coastguard Worker
30*635a8641SAndroid Build Coastguard Worker
31*635a8641SAndroid Build Coastguard Workerasync def generate_async(self, *args, **kwargs):
32*635a8641SAndroid Build Coastguard Worker    vars = dict(*args, **kwargs)
33*635a8641SAndroid Build Coastguard Worker    try:
34*635a8641SAndroid Build Coastguard Worker        async for event in self.root_render_func(self.new_context(vars)):
35*635a8641SAndroid Build Coastguard Worker            yield event
36*635a8641SAndroid Build Coastguard Worker    except Exception:
37*635a8641SAndroid Build Coastguard Worker        exc_info = sys.exc_info()
38*635a8641SAndroid Build Coastguard Worker    else:
39*635a8641SAndroid Build Coastguard Worker        return
40*635a8641SAndroid Build Coastguard Worker    yield self.environment.handle_exception(exc_info, True)
41*635a8641SAndroid Build Coastguard Worker
42*635a8641SAndroid Build Coastguard Worker
43*635a8641SAndroid Build Coastguard Workerdef wrap_generate_func(original_generate):
44*635a8641SAndroid Build Coastguard Worker    def _convert_generator(self, loop, args, kwargs):
45*635a8641SAndroid Build Coastguard Worker        async_gen = self.generate_async(*args, **kwargs)
46*635a8641SAndroid Build Coastguard Worker        try:
47*635a8641SAndroid Build Coastguard Worker            while 1:
48*635a8641SAndroid Build Coastguard Worker                yield loop.run_until_complete(async_gen.__anext__())
49*635a8641SAndroid Build Coastguard Worker        except StopAsyncIteration:
50*635a8641SAndroid Build Coastguard Worker            pass
51*635a8641SAndroid Build Coastguard Worker    def generate(self, *args, **kwargs):
52*635a8641SAndroid Build Coastguard Worker        if not self.environment.is_async:
53*635a8641SAndroid Build Coastguard Worker            return original_generate(self, *args, **kwargs)
54*635a8641SAndroid Build Coastguard Worker        return _convert_generator(self, asyncio.get_event_loop(), args, kwargs)
55*635a8641SAndroid Build Coastguard Worker    return update_wrapper(generate, original_generate)
56*635a8641SAndroid Build Coastguard Worker
57*635a8641SAndroid Build Coastguard Worker
58*635a8641SAndroid Build Coastguard Workerasync def render_async(self, *args, **kwargs):
59*635a8641SAndroid Build Coastguard Worker    if not self.environment.is_async:
60*635a8641SAndroid Build Coastguard Worker        raise RuntimeError('The environment was not created with async mode '
61*635a8641SAndroid Build Coastguard Worker                           'enabled.')
62*635a8641SAndroid Build Coastguard Worker
63*635a8641SAndroid Build Coastguard Worker    vars = dict(*args, **kwargs)
64*635a8641SAndroid Build Coastguard Worker    ctx = self.new_context(vars)
65*635a8641SAndroid Build Coastguard Worker
66*635a8641SAndroid Build Coastguard Worker    try:
67*635a8641SAndroid Build Coastguard Worker        return await concat_async(self.root_render_func(ctx))
68*635a8641SAndroid Build Coastguard Worker    except Exception:
69*635a8641SAndroid Build Coastguard Worker        exc_info = sys.exc_info()
70*635a8641SAndroid Build Coastguard Worker    return self.environment.handle_exception(exc_info, True)
71*635a8641SAndroid Build Coastguard Worker
72*635a8641SAndroid Build Coastguard Worker
73*635a8641SAndroid Build Coastguard Workerdef wrap_render_func(original_render):
74*635a8641SAndroid Build Coastguard Worker    def render(self, *args, **kwargs):
75*635a8641SAndroid Build Coastguard Worker        if not self.environment.is_async:
76*635a8641SAndroid Build Coastguard Worker            return original_render(self, *args, **kwargs)
77*635a8641SAndroid Build Coastguard Worker        loop = asyncio.get_event_loop()
78*635a8641SAndroid Build Coastguard Worker        return loop.run_until_complete(self.render_async(*args, **kwargs))
79*635a8641SAndroid Build Coastguard Worker    return update_wrapper(render, original_render)
80*635a8641SAndroid Build Coastguard Worker
81*635a8641SAndroid Build Coastguard Worker
82*635a8641SAndroid Build Coastguard Workerdef wrap_block_reference_call(original_call):
83*635a8641SAndroid Build Coastguard Worker    @internalcode
84*635a8641SAndroid Build Coastguard Worker    async def async_call(self):
85*635a8641SAndroid Build Coastguard Worker        rv = await concat_async(self._stack[self._depth](self._context))
86*635a8641SAndroid Build Coastguard Worker        if self._context.eval_ctx.autoescape:
87*635a8641SAndroid Build Coastguard Worker            rv = Markup(rv)
88*635a8641SAndroid Build Coastguard Worker        return rv
89*635a8641SAndroid Build Coastguard Worker
90*635a8641SAndroid Build Coastguard Worker    @internalcode
91*635a8641SAndroid Build Coastguard Worker    def __call__(self):
92*635a8641SAndroid Build Coastguard Worker        if not self._context.environment.is_async:
93*635a8641SAndroid Build Coastguard Worker            return original_call(self)
94*635a8641SAndroid Build Coastguard Worker        return async_call(self)
95*635a8641SAndroid Build Coastguard Worker
96*635a8641SAndroid Build Coastguard Worker    return update_wrapper(__call__, original_call)
97*635a8641SAndroid Build Coastguard Worker
98*635a8641SAndroid Build Coastguard Worker
99*635a8641SAndroid Build Coastguard Workerdef wrap_macro_invoke(original_invoke):
100*635a8641SAndroid Build Coastguard Worker    @internalcode
101*635a8641SAndroid Build Coastguard Worker    async def async_invoke(self, arguments, autoescape):
102*635a8641SAndroid Build Coastguard Worker        rv = await self._func(*arguments)
103*635a8641SAndroid Build Coastguard Worker        if autoescape:
104*635a8641SAndroid Build Coastguard Worker            rv = Markup(rv)
105*635a8641SAndroid Build Coastguard Worker        return rv
106*635a8641SAndroid Build Coastguard Worker
107*635a8641SAndroid Build Coastguard Worker    @internalcode
108*635a8641SAndroid Build Coastguard Worker    def _invoke(self, arguments, autoescape):
109*635a8641SAndroid Build Coastguard Worker        if not self._environment.is_async:
110*635a8641SAndroid Build Coastguard Worker            return original_invoke(self, arguments, autoescape)
111*635a8641SAndroid Build Coastguard Worker        return async_invoke(self, arguments, autoescape)
112*635a8641SAndroid Build Coastguard Worker    return update_wrapper(_invoke, original_invoke)
113*635a8641SAndroid Build Coastguard Worker
114*635a8641SAndroid Build Coastguard Worker
115*635a8641SAndroid Build Coastguard Worker@internalcode
116*635a8641SAndroid Build Coastguard Workerasync def get_default_module_async(self):
117*635a8641SAndroid Build Coastguard Worker    if self._module is not None:
118*635a8641SAndroid Build Coastguard Worker        return self._module
119*635a8641SAndroid Build Coastguard Worker    self._module = rv = await self.make_module_async()
120*635a8641SAndroid Build Coastguard Worker    return rv
121*635a8641SAndroid Build Coastguard Worker
122*635a8641SAndroid Build Coastguard Worker
123*635a8641SAndroid Build Coastguard Workerdef wrap_default_module(original_default_module):
124*635a8641SAndroid Build Coastguard Worker    @internalcode
125*635a8641SAndroid Build Coastguard Worker    def _get_default_module(self):
126*635a8641SAndroid Build Coastguard Worker        if self.environment.is_async:
127*635a8641SAndroid Build Coastguard Worker            raise RuntimeError('Template module attribute is unavailable '
128*635a8641SAndroid Build Coastguard Worker                               'in async mode')
129*635a8641SAndroid Build Coastguard Worker        return original_default_module(self)
130*635a8641SAndroid Build Coastguard Worker    return _get_default_module
131*635a8641SAndroid Build Coastguard Worker
132*635a8641SAndroid Build Coastguard Worker
133*635a8641SAndroid Build Coastguard Workerasync def make_module_async(self, vars=None, shared=False, locals=None):
134*635a8641SAndroid Build Coastguard Worker    context = self.new_context(vars, shared, locals)
135*635a8641SAndroid Build Coastguard Worker    body_stream = []
136*635a8641SAndroid Build Coastguard Worker    async for item in self.root_render_func(context):
137*635a8641SAndroid Build Coastguard Worker        body_stream.append(item)
138*635a8641SAndroid Build Coastguard Worker    return TemplateModule(self, context, body_stream)
139*635a8641SAndroid Build Coastguard Worker
140*635a8641SAndroid Build Coastguard Worker
141*635a8641SAndroid Build Coastguard Workerdef patch_template():
142*635a8641SAndroid Build Coastguard Worker    from jinja2 import Template
143*635a8641SAndroid Build Coastguard Worker    Template.generate = wrap_generate_func(Template.generate)
144*635a8641SAndroid Build Coastguard Worker    Template.generate_async = update_wrapper(
145*635a8641SAndroid Build Coastguard Worker        generate_async, Template.generate_async)
146*635a8641SAndroid Build Coastguard Worker    Template.render_async = update_wrapper(
147*635a8641SAndroid Build Coastguard Worker        render_async, Template.render_async)
148*635a8641SAndroid Build Coastguard Worker    Template.render = wrap_render_func(Template.render)
149*635a8641SAndroid Build Coastguard Worker    Template._get_default_module = wrap_default_module(
150*635a8641SAndroid Build Coastguard Worker        Template._get_default_module)
151*635a8641SAndroid Build Coastguard Worker    Template._get_default_module_async = get_default_module_async
152*635a8641SAndroid Build Coastguard Worker    Template.make_module_async = update_wrapper(
153*635a8641SAndroid Build Coastguard Worker        make_module_async, Template.make_module_async)
154*635a8641SAndroid Build Coastguard Worker
155*635a8641SAndroid Build Coastguard Worker
156*635a8641SAndroid Build Coastguard Workerdef patch_runtime():
157*635a8641SAndroid Build Coastguard Worker    from jinja2.runtime import BlockReference, Macro
158*635a8641SAndroid Build Coastguard Worker    BlockReference.__call__ = wrap_block_reference_call(
159*635a8641SAndroid Build Coastguard Worker        BlockReference.__call__)
160*635a8641SAndroid Build Coastguard Worker    Macro._invoke = wrap_macro_invoke(Macro._invoke)
161*635a8641SAndroid Build Coastguard Worker
162*635a8641SAndroid Build Coastguard Worker
163*635a8641SAndroid Build Coastguard Workerdef patch_filters():
164*635a8641SAndroid Build Coastguard Worker    from jinja2.filters import FILTERS
165*635a8641SAndroid Build Coastguard Worker    from jinja2.asyncfilters import ASYNC_FILTERS
166*635a8641SAndroid Build Coastguard Worker    FILTERS.update(ASYNC_FILTERS)
167*635a8641SAndroid Build Coastguard Worker
168*635a8641SAndroid Build Coastguard Worker
169*635a8641SAndroid Build Coastguard Workerdef patch_all():
170*635a8641SAndroid Build Coastguard Worker    patch_template()
171*635a8641SAndroid Build Coastguard Worker    patch_runtime()
172*635a8641SAndroid Build Coastguard Worker    patch_filters()
173*635a8641SAndroid Build Coastguard Worker
174*635a8641SAndroid Build Coastguard Worker
175*635a8641SAndroid Build Coastguard Workerasync def auto_await(value):
176*635a8641SAndroid Build Coastguard Worker    if inspect.isawaitable(value):
177*635a8641SAndroid Build Coastguard Worker        return await value
178*635a8641SAndroid Build Coastguard Worker    return value
179*635a8641SAndroid Build Coastguard Worker
180*635a8641SAndroid Build Coastguard Worker
181*635a8641SAndroid Build Coastguard Workerasync def auto_aiter(iterable):
182*635a8641SAndroid Build Coastguard Worker    if hasattr(iterable, '__aiter__'):
183*635a8641SAndroid Build Coastguard Worker        async for item in iterable:
184*635a8641SAndroid Build Coastguard Worker            yield item
185*635a8641SAndroid Build Coastguard Worker        return
186*635a8641SAndroid Build Coastguard Worker    for item in iterable:
187*635a8641SAndroid Build Coastguard Worker        yield item
188*635a8641SAndroid Build Coastguard Worker
189*635a8641SAndroid Build Coastguard Worker
190*635a8641SAndroid Build Coastguard Workerclass AsyncLoopContext(LoopContextBase):
191*635a8641SAndroid Build Coastguard Worker
192*635a8641SAndroid Build Coastguard Worker    def __init__(self, async_iterator, undefined, after, length, recurse=None,
193*635a8641SAndroid Build Coastguard Worker                 depth0=0):
194*635a8641SAndroid Build Coastguard Worker        LoopContextBase.__init__(self, undefined, recurse, depth0)
195*635a8641SAndroid Build Coastguard Worker        self._async_iterator = async_iterator
196*635a8641SAndroid Build Coastguard Worker        self._after = after
197*635a8641SAndroid Build Coastguard Worker        self._length = length
198*635a8641SAndroid Build Coastguard Worker
199*635a8641SAndroid Build Coastguard Worker    @property
200*635a8641SAndroid Build Coastguard Worker    def length(self):
201*635a8641SAndroid Build Coastguard Worker        if self._length is None:
202*635a8641SAndroid Build Coastguard Worker            raise TypeError('Loop length for some iterators cannot be '
203*635a8641SAndroid Build Coastguard Worker                            'lazily calculated in async mode')
204*635a8641SAndroid Build Coastguard Worker        return self._length
205*635a8641SAndroid Build Coastguard Worker
206*635a8641SAndroid Build Coastguard Worker    def __aiter__(self):
207*635a8641SAndroid Build Coastguard Worker        return AsyncLoopContextIterator(self)
208*635a8641SAndroid Build Coastguard Worker
209*635a8641SAndroid Build Coastguard Worker
210*635a8641SAndroid Build Coastguard Workerclass AsyncLoopContextIterator(object):
211*635a8641SAndroid Build Coastguard Worker    __slots__ = ('context',)
212*635a8641SAndroid Build Coastguard Worker
213*635a8641SAndroid Build Coastguard Worker    def __init__(self, context):
214*635a8641SAndroid Build Coastguard Worker        self.context = context
215*635a8641SAndroid Build Coastguard Worker
216*635a8641SAndroid Build Coastguard Worker    def __aiter__(self):
217*635a8641SAndroid Build Coastguard Worker        return self
218*635a8641SAndroid Build Coastguard Worker
219*635a8641SAndroid Build Coastguard Worker    async def __anext__(self):
220*635a8641SAndroid Build Coastguard Worker        ctx = self.context
221*635a8641SAndroid Build Coastguard Worker        ctx.index0 += 1
222*635a8641SAndroid Build Coastguard Worker        if ctx._after is _last_iteration:
223*635a8641SAndroid Build Coastguard Worker            raise StopAsyncIteration()
224*635a8641SAndroid Build Coastguard Worker        ctx._before = ctx._current
225*635a8641SAndroid Build Coastguard Worker        ctx._current = ctx._after
226*635a8641SAndroid Build Coastguard Worker        try:
227*635a8641SAndroid Build Coastguard Worker            ctx._after = await ctx._async_iterator.__anext__()
228*635a8641SAndroid Build Coastguard Worker        except StopAsyncIteration:
229*635a8641SAndroid Build Coastguard Worker            ctx._after = _last_iteration
230*635a8641SAndroid Build Coastguard Worker        return ctx._current, ctx
231*635a8641SAndroid Build Coastguard Worker
232*635a8641SAndroid Build Coastguard Worker
233*635a8641SAndroid Build Coastguard Workerasync def make_async_loop_context(iterable, undefined, recurse=None, depth0=0):
234*635a8641SAndroid Build Coastguard Worker    # Length is more complicated and less efficient in async mode.  The
235*635a8641SAndroid Build Coastguard Worker    # reason for this is that we cannot know if length will be used
236*635a8641SAndroid Build Coastguard Worker    # upfront but because length is a property we cannot lazily execute it
237*635a8641SAndroid Build Coastguard Worker    # later.  This means that we need to buffer it up and measure :(
238*635a8641SAndroid Build Coastguard Worker    #
239*635a8641SAndroid Build Coastguard Worker    # We however only do this for actual iterators, not for async
240*635a8641SAndroid Build Coastguard Worker    # iterators as blocking here does not seem like the best idea in the
241*635a8641SAndroid Build Coastguard Worker    # world.
242*635a8641SAndroid Build Coastguard Worker    try:
243*635a8641SAndroid Build Coastguard Worker        length = len(iterable)
244*635a8641SAndroid Build Coastguard Worker    except (TypeError, AttributeError):
245*635a8641SAndroid Build Coastguard Worker        if not hasattr(iterable, '__aiter__'):
246*635a8641SAndroid Build Coastguard Worker            iterable = tuple(iterable)
247*635a8641SAndroid Build Coastguard Worker            length = len(iterable)
248*635a8641SAndroid Build Coastguard Worker        else:
249*635a8641SAndroid Build Coastguard Worker            length = None
250*635a8641SAndroid Build Coastguard Worker    async_iterator = auto_aiter(iterable)
251*635a8641SAndroid Build Coastguard Worker    try:
252*635a8641SAndroid Build Coastguard Worker        after = await async_iterator.__anext__()
253*635a8641SAndroid Build Coastguard Worker    except StopAsyncIteration:
254*635a8641SAndroid Build Coastguard Worker        after = _last_iteration
255*635a8641SAndroid Build Coastguard Worker    return AsyncLoopContext(async_iterator, undefined, after, length, recurse,
256*635a8641SAndroid Build Coastguard Worker                            depth0)
257