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