asyncfilters.py 4.2 KB

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