• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from functools import wraps
2
3from jinja2.asyncsupport import auto_aiter
4from jinja2 import filters
5
6
7async def auto_to_seq(value):
8    seq = []
9    if hasattr(value, '__aiter__'):
10        async for item in value:
11            seq.append(item)
12    else:
13        for item in value:
14            seq.append(item)
15    return seq
16
17
18async def async_select_or_reject(args, kwargs, modfunc, lookup_attr):
19    seq, func = filters.prepare_select_or_reject(
20        args, kwargs, modfunc, lookup_attr)
21    if seq:
22        async for item in auto_aiter(seq):
23            if func(item):
24                yield item
25
26
27def dualfilter(normal_filter, async_filter):
28    wrap_evalctx = False
29    if getattr(normal_filter, 'environmentfilter', False):
30        is_async = lambda args: args[0].is_async
31        wrap_evalctx = False
32    else:
33        if not getattr(normal_filter, 'evalcontextfilter', False) and \
34           not getattr(normal_filter, 'contextfilter', False):
35            wrap_evalctx = True
36        is_async = lambda args: args[0].environment.is_async
37
38    @wraps(normal_filter)
39    def wrapper(*args, **kwargs):
40        b = is_async(args)
41        if wrap_evalctx:
42            args = args[1:]
43        if b:
44            return async_filter(*args, **kwargs)
45        return normal_filter(*args, **kwargs)
46
47    if wrap_evalctx:
48        wrapper.evalcontextfilter = True
49
50    wrapper.asyncfiltervariant = True
51
52    return wrapper
53
54
55def asyncfiltervariant(original):
56    def decorator(f):
57        return dualfilter(original, f)
58    return decorator
59
60
61@asyncfiltervariant(filters.do_first)
62async def do_first(environment, seq):
63    try:
64        return await auto_aiter(seq).__anext__()
65    except StopAsyncIteration:
66        return environment.undefined('No first item, sequence was empty.')
67
68
69@asyncfiltervariant(filters.do_groupby)
70async def do_groupby(environment, value, attribute):
71    expr = filters.make_attrgetter(environment, attribute)
72    return [filters._GroupTuple(key, await auto_to_seq(values))
73            for key, values in filters.groupby(sorted(
74                await auto_to_seq(value), key=expr), expr)]
75
76
77@asyncfiltervariant(filters.do_join)
78async def do_join(eval_ctx, value, d=u'', attribute=None):
79    return filters.do_join(eval_ctx, await auto_to_seq(value), d, attribute)
80
81
82@asyncfiltervariant(filters.do_list)
83async def do_list(value):
84    return await auto_to_seq(value)
85
86
87@asyncfiltervariant(filters.do_reject)
88async def do_reject(*args, **kwargs):
89    return async_select_or_reject(args, kwargs, lambda x: not x, False)
90
91
92@asyncfiltervariant(filters.do_rejectattr)
93async def do_rejectattr(*args, **kwargs):
94    return async_select_or_reject(args, kwargs, lambda x: not x, True)
95
96
97@asyncfiltervariant(filters.do_select)
98async def do_select(*args, **kwargs):
99    return async_select_or_reject(args, kwargs, lambda x: x, False)
100
101
102@asyncfiltervariant(filters.do_selectattr)
103async def do_selectattr(*args, **kwargs):
104    return async_select_or_reject(args, kwargs, lambda x: x, True)
105
106
107@asyncfiltervariant(filters.do_map)
108async def do_map(*args, **kwargs):
109    seq, func = filters.prepare_map(args, kwargs)
110    if seq:
111        async for item in auto_aiter(seq):
112            yield func(item)
113
114
115@asyncfiltervariant(filters.do_sum)
116async def do_sum(environment, iterable, attribute=None, start=0):
117    rv = start
118    if attribute is not None:
119        func = filters.make_attrgetter(environment, attribute)
120    else:
121        func = lambda x: x
122    async for item in auto_aiter(iterable):
123        rv += func(item)
124    return rv
125
126
127@asyncfiltervariant(filters.do_slice)
128async def do_slice(value, slices, fill_with=None):
129    return filters.do_slice(await auto_to_seq(value), slices, fill_with)
130
131
132ASYNC_FILTERS = {
133    'first':        do_first,
134    'groupby':      do_groupby,
135    'join':         do_join,
136    'list':         do_list,
137    # we intentionally do not support do_last because that would be
138    # ridiculous
139    'reject':       do_reject,
140    'rejectattr':   do_rejectattr,
141    'map':          do_map,
142    'select':       do_select,
143    'selectattr':   do_selectattr,
144    'sum':          do_sum,
145    'slice':        do_slice,
146}
147