python-iterutils 0.1.11__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.1.11
3
+ Version: 0.2.1
4
4
  Summary: Python another itertools.
5
5
  Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-iterutils
6
6
  License: MIT
@@ -2,14 +2,16 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 1, 11)
5
+ __version__ = (0, 2, 1)
6
6
  __all__ = [
7
- "Return", "Yield", "YieldFrom", "iterable", "async_iterable", "foreach", "async_foreach",
8
- "through", "async_through", "flatten", "async_flatten", "collect", "async_collect",
9
- "group_collect", "async_group_collect", "map", "filter", "reduce", "zip", "chunked",
10
- "iter_unique", "async_iter_unique", "wrap_iter", "wrap_aiter", "acc_step", "cut_iter",
11
- "iter_gen_step", "iter_gen_step_async", "run_gen_step", "run_gen_step_iter",
12
- "as_gen_step", "bfs_gen", "with_iter_next", "backgroud_loop",
7
+ "Return", "Yield", "YieldFrom", "iterable", "async_iterable",
8
+ "foreach", "async_foreach", "through", "async_through", "flatten",
9
+ "async_flatten", "collect", "async_collect", "group_collect",
10
+ "async_group_collect", "map", "filter", "reduce", "zip", "chunked",
11
+ "iter_unique", "async_iter_unique", "wrap_iter", "wrap_aiter",
12
+ "acc_step", "cut_iter", "iter_gen_step", "iter_gen_step_async",
13
+ "run_gen_step", "run_gen_step_iter", "as_gen_step", "bfs_gen",
14
+ "with_iter_next", "backgroud_loop",
13
15
  ]
14
16
 
15
17
  from abc import ABC, abstractmethod
@@ -17,11 +19,14 @@ from asyncio import create_task, sleep as async_sleep, to_thread
17
19
  from builtins import map as _map, filter as _filter, zip as _zip
18
20
  from collections import defaultdict, deque
19
21
  from collections.abc import (
20
- AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable, Collection, Container,
21
- Coroutine, Generator, Iterable, Iterator, Mapping, MutableMapping, MutableSet,
22
- MutableSequence, Sequence, ValuesView,
22
+ AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable,
23
+ Collection, Container, Coroutine, Generator, Iterable, Iterator,
24
+ Mapping, MutableMapping, MutableSet, MutableSequence, Sequence,
25
+ ValuesView,
26
+ )
27
+ from contextlib import (
28
+ asynccontextmanager, contextmanager, ExitStack, AsyncExitStack,
23
29
  )
24
- from contextlib import asynccontextmanager, contextmanager, ExitStack, AsyncExitStack
25
30
  from copy import copy
26
31
  from dataclasses import dataclass
27
32
  from itertools import batched, pairwise
@@ -30,26 +35,42 @@ from sys import _getframe
30
35
  from _thread import start_new_thread
31
36
  from time import sleep, time
32
37
  from types import FrameType
33
- from typing import cast, overload, Any, AsyncContextManager, ContextManager, Literal, Protocol
38
+ from typing import (
39
+ cast, overload, Any, AsyncContextManager, ContextManager, Literal,
40
+ Protocol,
41
+ )
34
42
 
35
43
  from asynctools import (
36
- async_filter, async_map, async_reduce, async_zip, async_batched, ensure_async, ensure_aiter,
37
- collect as async_collect,
44
+ async_filter, async_map, async_reduce, async_zip, async_batched,
45
+ ensure_async, ensure_aiter, collect as async_collect,
38
46
  )
39
47
  from decotools import optional
40
48
  from texttools import format_time
41
- from undefined import undefined, Undefined
49
+ from undefined import undefined
42
50
 
43
51
 
44
52
  class SupportsBool(Protocol):
53
+ """
54
+ """
45
55
  def __bool__(self, /) -> bool: ...
46
56
 
47
57
 
48
- @dataclass(slots=True, frozen=True)
58
+ class Reraised(BaseException):
59
+ """
60
+ """
61
+ def __init__(self, exc: BaseException, /):
62
+ if isinstance(exc, Reraised):
63
+ exc = exc.exception
64
+ self.exception: BaseException = exc
65
+
66
+
67
+ @dataclass(slots=True, frozen=True, unsafe_hash=True)
49
68
  class YieldBase(ABC):
69
+ """
70
+ """
50
71
  value: Any
51
- identity: bool = False
52
- try_call_me: bool = True
72
+ may_await: None | bool | Literal[1] = False
73
+ may_call: None | bool | Literal[1] = None
53
74
 
54
75
  @property
55
76
  @abstractmethod
@@ -57,25 +78,30 @@ class YieldBase(ABC):
57
78
  ...
58
79
 
59
80
 
60
- class Reraised(BaseException):
61
-
62
- def __init__(self, exc: BaseException, /):
63
- self.exception = exc
64
-
65
-
66
81
  class Return(YieldBase):
82
+ """
83
+ """
84
+ __slots__ = ()
67
85
  yield_type = 0
68
86
 
69
87
 
70
88
  class Yield(YieldBase):
89
+ """
90
+ """
91
+ __slots__ = ()
71
92
  yield_type = 1
72
93
 
73
94
 
74
95
  class YieldFrom(YieldBase):
96
+ """
97
+ """
98
+ __slots__ = ()
75
99
  yield_type = 2
76
100
 
77
101
 
78
102
  def iterable(iterable, /) -> bool:
103
+ """
104
+ """
79
105
  try:
80
106
  return isinstance(iter(iterable), Iterable)
81
107
  except TypeError:
@@ -83,6 +109,8 @@ def iterable(iterable, /) -> bool:
83
109
 
84
110
 
85
111
  def async_iterable(iterable, /) -> bool:
112
+ """
113
+ """
86
114
  try:
87
115
  return isinstance(iter(iterable), AsyncIterable)
88
116
  except TypeError:
@@ -95,6 +123,8 @@ def foreach(
95
123
  /,
96
124
  *iterables: Iterable | AsyncIterable,
97
125
  ):
126
+ """
127
+ """
98
128
  if not (isinstance(iterable, Iterable) and all(isinstance(it, Iterable) for it in iterables)):
99
129
  return async_foreach(value, iterable, *iterables)
100
130
  if iterables:
@@ -112,6 +142,8 @@ async def async_foreach(
112
142
  *iterables: Iterable | AsyncIterable,
113
143
  threaded: bool = False,
114
144
  ):
145
+ """
146
+ """
115
147
  value = ensure_async(value, threaded=threaded)
116
148
  if iterables:
117
149
  async for args in async_zip(iterable, *iterables, threaded=threaded):
@@ -126,6 +158,8 @@ def through(
126
158
  /,
127
159
  take_while: None | Callable = None,
128
160
  ):
161
+ """
162
+ """
129
163
  if not isinstance(iterable, Iterable):
130
164
  return async_through(iterable, take_while)
131
165
  if take_while is None:
@@ -143,6 +177,8 @@ async def async_through(
143
177
  take_while: None | Callable = None,
144
178
  threaded: bool = False,
145
179
  ):
180
+ """
181
+ """
146
182
  iterable = ensure_aiter(iterable, threaded=threaded)
147
183
  if take_while is None:
148
184
  async for _ in iterable:
@@ -176,6 +212,8 @@ def flatten(
176
212
  /,
177
213
  exclude_types: type | tuple[type, ...] = (Buffer, str),
178
214
  ) -> Iterator | AsyncIterator:
215
+ """
216
+ """
179
217
  if not isinstance(iterable, Iterable):
180
218
  return async_flatten(iterable, exclude_types)
181
219
  def gen(iterable):
@@ -193,6 +231,8 @@ async def async_flatten(
193
231
  exclude_types: type | tuple[type, ...] = (Buffer, str),
194
232
  threaded: bool = False,
195
233
  ) -> AsyncIterator:
234
+ """
235
+ """
196
236
  async for e in ensure_aiter(iterable, threaded=threaded):
197
237
  if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
198
238
  async for e in async_flatten(e, exclude_types, threaded=threaded):
@@ -234,6 +274,8 @@ def collect(
234
274
  /,
235
275
  rettype: Callable[[Iterable], Collection] = list,
236
276
  ) -> Collection | Coroutine[Any, Any, Collection]:
277
+ """
278
+ """
237
279
  if not isinstance(iterable, Iterable):
238
280
  return async_collect(iterable, rettype)
239
281
  return rettype(iterable)
@@ -272,6 +314,8 @@ def group_collect[K, V, C: Container, M: MutableMapping](
272
314
  mapping: None | M = None,
273
315
  factory: None | C | Callable[[], C] = None,
274
316
  ) -> dict[K, C] | M | Coroutine[Any, Any, dict[K, C]] | Coroutine[Any, Any, M]:
317
+ """
318
+ """
275
319
  if not isinstance(iterable, Iterable):
276
320
  return async_group_collect(iterable, mapping, factory)
277
321
  if factory is None:
@@ -329,6 +373,8 @@ async def async_group_collect[K, V, C: Container, M: MutableMapping](
329
373
  factory: None | C | Callable[[], C] = None,
330
374
  threaded: bool = False,
331
375
  ) -> dict[K, C] | M:
376
+ """
377
+ """
332
378
  iterable = ensure_aiter(iterable, threaded=threaded)
333
379
  if factory is None:
334
380
  if isinstance(mapping, defaultdict):
@@ -368,7 +414,9 @@ def map(
368
414
  iterable: Iterable | AsyncIterable,
369
415
  /,
370
416
  *iterables: Iterable | AsyncIterable,
371
- ):
417
+ ):
418
+ """
419
+ """
372
420
  if (
373
421
  iscoroutinefunction(function) or
374
422
  isinstance(iterable, AsyncIterable) or
@@ -393,6 +441,8 @@ def filter(
393
441
  iterable: Iterable | AsyncIterable,
394
442
  /,
395
443
  ):
444
+ """
445
+ """
396
446
  if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
397
447
  return async_filter(function, iterable)
398
448
  return _filter(function, iterable)
@@ -401,9 +451,11 @@ def filter(
401
451
  def reduce(
402
452
  function: Callable,
403
453
  iterable: Iterable | AsyncIterable,
404
- initial = undefined,
454
+ initial: Any = undefined,
405
455
  /,
406
456
  ):
457
+ """
458
+ """
407
459
  if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
408
460
  return async_reduce(function, iterable, initial)
409
461
  from functools import reduce
@@ -417,6 +469,8 @@ def zip(
417
469
  /,
418
470
  *iterables: Iterable | AsyncIterable,
419
471
  ):
472
+ """
473
+ """
420
474
  if isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
421
475
  return async_zip(iterable, *iterables)
422
476
  return _zip(iterable, *iterables)
@@ -441,6 +495,8 @@ def chunked[T](
441
495
  n: int = 1,
442
496
  /,
443
497
  ) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
498
+ """
499
+ """
444
500
  if n < 0:
445
501
  n = 1
446
502
  if isinstance(iterable, Sequence):
@@ -472,6 +528,8 @@ def iter_unique[T](
472
528
  /,
473
529
  seen: None | MutableSet = None,
474
530
  ) -> Iterator[T] | AsyncIterator[T]:
531
+ """
532
+ """
475
533
  if not isinstance(iterable, Iterable):
476
534
  return async_iter_unique(iterable, seen)
477
535
  if seen is None:
@@ -491,6 +549,8 @@ async def async_iter_unique[T](
491
549
  seen: None | MutableSet = None,
492
550
  threaded: bool = False,
493
551
  ) -> AsyncIterator[T]:
552
+ """
553
+ """
494
554
  if seen is None:
495
555
  seen = set()
496
556
  add = seen.add
@@ -506,8 +566,6 @@ def wrap_iter[T](
506
566
  /,
507
567
  callprev: None | Callable[[T], Any] = None,
508
568
  callnext: None | Callable[[T], Any] = None,
509
- callenter: None | Callable[[Iterable[T]], Any] = None,
510
- callexit: None | Callable[[Iterable[T], None | BaseException], Any] = None,
511
569
  ) -> Iterator[T]:
512
570
  ...
513
571
  @overload
@@ -516,8 +574,6 @@ def wrap_iter[T](
516
574
  /,
517
575
  callprev: None | Callable[[T], Any] = None,
518
576
  callnext: None | Callable[[T], Any] = None,
519
- callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
520
- callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
521
577
  ) -> AsyncIterator[T]:
522
578
  ...
523
579
  def wrap_iter[T](
@@ -525,47 +581,24 @@ def wrap_iter[T](
525
581
  /,
526
582
  callprev: None | Callable[[T], Any] = None,
527
583
  callnext: None | Callable[[T], Any] = None,
528
- callenter: None | Callable[[Iterable[T]], Any] | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
529
- callexit: ( None | Callable[[Iterable[T], None | BaseException], Any] |
530
- Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] ) = None,
531
584
  ) -> Iterator[T] | AsyncIterator[T]:
585
+ """
586
+ """
532
587
  if not isinstance(iterable, Iterable):
533
588
  return wrap_aiter(
534
589
  iterable,
535
590
  callprev=callprev,
536
591
  callnext=callnext,
537
- callenter=callenter, # type: ignore
538
- callexit=callexit, # type: ignore
539
592
  )
540
593
  if not callable(callprev):
541
594
  callprev = None
542
595
  if not callable(callnext):
543
596
  callnext = None
544
597
  def gen():
545
- try:
546
- if callable(callenter):
547
- callenter(iterable)
548
- for e in iterable:
549
- if callprev:
550
- try:
551
- callprev(e)
552
- except (StopIteration, GeneratorExit):
553
- break
554
- yield e
555
- if callnext:
556
- try:
557
- callnext(e)
558
- except (StopIteration, GeneratorExit):
559
- break
560
- except BaseException as e:
561
- if callable(callexit):
562
- if not callexit(iterable, e):
563
- raise
564
- else:
565
- raise
566
- finally:
567
- if callable(callexit):
568
- callexit(iterable, None)
598
+ for e in iterable:
599
+ callprev and callprev(e)
600
+ yield e
601
+ callnext and callnext(e)
569
602
  return gen()
570
603
 
571
604
 
@@ -574,34 +607,16 @@ async def wrap_aiter[T](
574
607
  /,
575
608
  callprev: None | Callable[[T], Any] = None,
576
609
  callnext: None | Callable[[T], Any] = None,
577
- callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
578
- callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
579
610
  threaded: bool = False,
580
611
  ) -> AsyncIterator[T]:
612
+ """
613
+ """
581
614
  callprev = ensure_async(callprev, threaded=threaded) if callable(callprev) else None
582
615
  callnext = ensure_async(callnext, threaded=threaded) if callable(callnext) else None
583
- try:
584
- async for e in ensure_aiter(iterable, threaded=threaded):
585
- if callprev:
586
- try:
587
- await callprev(e)
588
- except (StopAsyncIteration, GeneratorExit):
589
- break
590
- yield e
591
- if callnext:
592
- try:
593
- await callnext(e)
594
- except (StopAsyncIteration, GeneratorExit):
595
- break
596
- except BaseException as e:
597
- if callable(callexit):
598
- if not await ensure_async(callexit, threaded=threaded)(iterable, e):
599
- raise
600
- else:
601
- raise
602
- finally:
603
- if callable(callexit):
604
- await ensure_async(callexit, threaded=threaded)(iterable, None)
616
+ async for e in ensure_aiter(iterable, threaded=threaded):
617
+ callprev and await callprev(e)
618
+ yield e
619
+ callnext and await callnext(e)
605
620
 
606
621
 
607
622
  def acc_step(
@@ -609,6 +624,8 @@ def acc_step(
609
624
  stop: None | int = None,
610
625
  step: int = 1,
611
626
  ) -> Iterator[tuple[int, int, int]]:
627
+ """
628
+ """
612
629
  if stop is None:
613
630
  start, stop = 0, start
614
631
  for i in range(start + step, stop, step):
@@ -622,6 +639,8 @@ def cut_iter(
622
639
  stop: None | int = None,
623
640
  step: int = 1,
624
641
  ) -> Iterator[tuple[int, int]]:
642
+ """
643
+ """
625
644
  if stop is None:
626
645
  start, stop = 0, start
627
646
  for start in range(start + step, stop, step):
@@ -650,20 +669,25 @@ def _get_async(back: int = 2) -> bool:
650
669
  @overload
651
670
  def call_as_async[**Args, T: Coroutine](
652
671
  func: Callable[Args, T], /,
672
+ may_await: bool | Literal[1] = False,
653
673
  threaded: bool = False,
654
674
  ) -> Callable[Args, T]:
655
675
  ...
656
676
  @overload
657
677
  def call_as_async[**Args, T](
658
678
  func: Callable[Args, T], /,
679
+ may_await: bool | Literal[1] = False,
659
680
  threaded: bool = False,
660
681
  ) -> Callable[Args, Coroutine[Any, Any, T]]:
661
682
  ...
662
683
  def call_as_async[**Args, T](
663
684
  func: Callable[Args, T],
664
685
  /,
686
+ may_await: bool | Literal[1] = False,
665
687
  threaded: bool = False,
666
688
  ) -> Callable[Args, T] | Callable[Args, Coroutine[Any, Any, T]]:
689
+ """
690
+ """
667
691
  if iscoroutinefunction(func):
668
692
  return func
669
693
  def wraps(*args, **kwds):
@@ -671,32 +695,35 @@ def call_as_async[**Args, T](
671
695
  return func(*args, **kwds)
672
696
  except (StopIteration, StopAsyncIteration) as e:
673
697
  raise Reraised(e) from e
674
- if threaded:
675
- async def wrapper(*args, **kwds):
676
- return await to_thread(wraps, *args, **kwds)
677
- else:
678
- async def wrapper(*args, **kwds):
679
- return wraps(*args, **kwds)
698
+ async def wrapper(*args, **kwds):
699
+ if threaded:
700
+ value = await to_thread(wraps, *args, **kwds)
701
+ else:
702
+ value = wraps(*args, **kwds)
703
+ if may_await is 1 or may_await and isawaitable(value):
704
+ value = await value
705
+ return value
680
706
  return wrapper
681
707
 
682
708
 
683
709
  def iter_gen_step(
684
710
  gen_step: Generator | Callable[[], Generator],
711
+ may_call: bool | Literal[1] = True,
685
712
  ):
713
+ """
714
+ """
686
715
  if callable(gen_step):
687
- gen = gen_step()
688
- else:
689
- gen = gen_step
690
- send = gen.send
691
- throw = gen.throw
692
- close = gen.close
716
+ gen_step = gen_step()
717
+ send = gen_step.send
718
+ throw = gen_step.throw
719
+ close = gen_step.close
720
+ value: Any = None
693
721
  try:
694
- value = send(None)
695
722
  while True:
696
723
  if isinstance(value, YieldBase):
697
724
  raise StopIteration(value)
698
725
  try:
699
- if callable(value):
726
+ if may_call is 1 or may_call and callable(value):
700
727
  value = value()
701
728
  except BaseException as e:
702
729
  value = throw(e)
@@ -705,11 +732,12 @@ def iter_gen_step(
705
732
  yield value
706
733
  except StopIteration as e:
707
734
  value = e.value
708
- try_call_me = True
709
735
  if isinstance(value, YieldBase):
710
- try_call_me = value.try_call_me
736
+ maybe_callable = value.may_call
737
+ if maybe_callable is not None:
738
+ may_call = maybe_callable
711
739
  value = value.value
712
- if callable(value) and try_call_me:
740
+ if may_call is 1 or may_call and callable(value):
713
741
  try:
714
742
  value = value()
715
743
  except BaseException as e:
@@ -718,32 +746,33 @@ def iter_gen_step(
718
746
  except BaseException as e:
719
747
  raise Reraised(e) from e
720
748
  yield value
721
- except BaseException as e:
722
- raise Reraised(e) from e
723
749
  finally:
724
750
  close()
725
751
 
726
752
 
727
753
  async def iter_gen_step_async(
728
754
  gen_step: Generator | Callable[[], Generator],
755
+ may_await: bool | Literal[1] = True,
756
+ may_call: bool | Literal[1] = True,
729
757
  threaded: bool = False,
730
758
  ):
759
+ """
760
+ """
731
761
  if callable(gen_step):
732
- gen = gen_step()
733
- else:
734
- gen = gen_step
735
- send: Callable = call_as_async(gen.send, threaded=threaded)
736
- throw: Callable = call_as_async(gen.throw, threaded=threaded)
737
- close: Callable = call_as_async(gen.close, threaded=threaded)
762
+ gen_step = gen_step()
763
+ send: Callable = call_as_async(gen_step.send, threaded=threaded)
764
+ throw: Callable = call_as_async(gen_step.throw, threaded=threaded)
765
+ close: Callable = call_as_async(gen_step.close, threaded=threaded)
766
+ value: Any = None
738
767
  try:
739
- value = await send(None)
740
768
  while True:
741
769
  if isinstance(value, YieldBase):
742
770
  raise StopIteration(value)
743
771
  try:
744
- if callable(value):
745
- value = await call_as_async(value, threaded=threaded)()
746
- if isawaitable(value):
772
+ if may_call is 1 or may_call and callable(value):
773
+ value = await call_as_async(
774
+ value, may_await=may_await, threaded=threaded)()
775
+ elif may_await is 1 or may_await and isawaitable(value):
747
776
  value = await value
748
777
  except BaseException as e:
749
778
  if isinstance(e, Reraised):
@@ -757,16 +786,19 @@ async def iter_gen_step_async(
757
786
  e = e.exception
758
787
  if isinstance(e, StopIteration):
759
788
  value = e.value
760
- identity = False
761
- try_call_me = True
762
789
  if isinstance(value, YieldBase):
763
- identity = value.identity
764
- try_call_me = value.try_call_me
790
+ maybe_awaitable = value.may_await
791
+ if maybe_awaitable is not None:
792
+ may_await = maybe_awaitable
793
+ maybe_callable = value.may_call
794
+ if maybe_callable is not None:
795
+ may_call = maybe_callable
765
796
  value = value.value
766
797
  try:
767
- if callable(value) and try_call_me:
768
- value = await call_as_async(value, threaded=threaded)()
769
- if not identity and isawaitable(value):
798
+ if may_call is 1 or may_call and callable(value):
799
+ value = await call_as_async(
800
+ value, may_await=may_await, threaded=threaded)()
801
+ elif may_await is 1 or may_await and isawaitable(value):
770
802
  value = await value
771
803
  except BaseException as e:
772
804
  try:
@@ -780,36 +812,44 @@ async def iter_gen_step_async(
780
812
  await close()
781
813
 
782
814
 
783
- def run_gen_step[T](
815
+ def run_gen_step(
784
816
  gen_step: Generator | Callable[[], Generator],
817
+ may_await: bool | Literal[1] = True,
818
+ may_call: bool | Literal[1] = True,
785
819
  threaded: bool = False,
786
- running_flag: None | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] | SupportsBool = None,
820
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
787
821
  *,
788
822
  async_: None | bool = None,
789
823
  ):
824
+ """
825
+ """
790
826
  if async_ is None:
791
827
  async_ = _get_async()
792
828
  if async_:
793
829
  async def process():
794
- gen = iter_gen_step_async(gen_step, threaded=threaded)
830
+ gen = iter_gen_step_async(
831
+ gen_step,
832
+ may_await=may_await,
833
+ may_call=may_call,
834
+ threaded=threaded,
835
+ )
795
836
  try:
796
837
  if running_flag is None:
797
838
  async for value in gen:
798
839
  pass
799
840
  elif callable(running_flag):
800
- pred = running_flag()
801
- if not ((await pred) if isawaitable(pred) else pred):
841
+ pred = call_as_async(running_flag, threaded=threaded)
842
+ if not await pred():
802
843
  raise RuntimeError("stop before starting", gen) from StopIteration()
803
844
  async for value in gen:
804
- pred = running_flag()
805
- if not ((await pred) if isawaitable(pred) else pred):
845
+ if not await pred():
806
846
  raise RuntimeError("stop midway", gen) from StopIteration(value)
807
- else:
847
+ else:
848
+ if not running_flag:
849
+ raise RuntimeError("stop before starting", gen) from StopIteration()
850
+ async for value in gen:
808
851
  if not running_flag:
809
- raise RuntimeError("stop before starting", gen) from StopIteration()
810
- async for value in gen:
811
- if not running_flag:
812
- raise RuntimeError("stop midway", gen) from StopIteration(value)
852
+ raise RuntimeError("stop midway", gen) from StopIteration(value)
813
853
  return value
814
854
  except Reraised as e:
815
855
  raise e.exception
@@ -819,7 +859,7 @@ def run_gen_step[T](
819
859
  raise
820
860
  return process()
821
861
  else:
822
- gen = iter_gen_step(gen_step)
862
+ gen = iter_gen_step(gen_step, may_call=may_call)
823
863
  try:
824
864
  if running_flag is None:
825
865
  for value in gen:
@@ -844,6 +884,8 @@ def run_gen_step[T](
844
884
  @overload
845
885
  def run_gen_step_iter(
846
886
  gen_step: Generator | Callable[[], Generator],
887
+ may_await: bool | Literal[1] = True,
888
+ may_call: bool | Literal[1] = True,
847
889
  threaded: bool = False,
848
890
  *,
849
891
  async_: None = None,
@@ -852,6 +894,8 @@ def run_gen_step_iter(
852
894
  @overload
853
895
  def run_gen_step_iter(
854
896
  gen_step: Generator | Callable[[], Generator],
897
+ may_await: bool | Literal[1] = True,
898
+ may_call: bool | Literal[1] = True,
855
899
  threaded: bool = False,
856
900
  *,
857
901
  async_: Literal[False],
@@ -860,6 +904,8 @@ def run_gen_step_iter(
860
904
  @overload
861
905
  def run_gen_step_iter(
862
906
  gen_step: Generator | Callable[[], Generator],
907
+ may_await: bool | Literal[1] = True,
908
+ may_call: bool | Literal[1] = True,
863
909
  threaded: bool = False,
864
910
  *,
865
911
  async_: Literal[True],
@@ -867,44 +913,54 @@ def run_gen_step_iter(
867
913
  ...
868
914
  def run_gen_step_iter(
869
915
  gen_step: Generator | Callable[[], Generator],
916
+ may_await: bool | Literal[1] = True,
917
+ may_call: bool | Literal[1] = True,
870
918
  threaded: bool = False,
871
919
  *,
872
920
  async_: None | bool = None,
873
921
  ) -> Iterator | AsyncIterator:
922
+ """
923
+ """
874
924
  if async_ is None:
875
925
  async_ = _get_async()
876
- if callable(gen_step):
877
- gen = gen_step()
878
- else:
879
- gen = gen_step
926
+ gen = gen_step() if callable(gen_step) else gen_step
880
927
  send: Callable = gen.send
881
928
  throw: Callable = gen.throw
882
929
  close: Callable = gen.close
930
+ default_may_await = may_await
931
+ default_may_call = may_call
883
932
  if async_:
884
933
  send = call_as_async(send, threaded=threaded)
885
934
  throw = call_as_async(throw, threaded=threaded)
886
935
  close = call_as_async(close, threaded=threaded)
887
- async def process():
888
- async def extract(value):
889
- identity = False
890
- try_call_me = True
891
- yield_type = -1
892
- if isinstance(value, YieldBase):
893
- identity = value.identity
894
- try_call_me = value.try_call_me
895
- yield_type = value.yield_type
896
- value = value.value
897
- if try_call_me and callable(value):
898
- value = await call_as_async(value, threaded=threaded)()
899
- if not identity and isawaitable(value):
900
- value = await value
901
- return yield_type, value
936
+ async def aextract(value, /):
937
+ yield_type = -1
938
+ may_await = default_may_await
939
+ may_call = default_may_call
940
+ if isinstance(value, YieldBase):
941
+ yield_type = value.yield_type
942
+ maybe_awaitable = value.may_await
943
+ if maybe_awaitable is not None:
944
+ may_await = maybe_awaitable
945
+ maybe_callable = value.may_call
946
+ if maybe_callable is not None:
947
+ may_call = maybe_callable
948
+ value = value.value
949
+ if may_call is 1 or may_call and callable(value):
950
+ value = await call_as_async(
951
+ value, may_await=may_await, threaded=threaded)()
952
+ elif may_await is 1 or may_await and isawaitable(value):
953
+ value = await value
954
+ return yield_type, value
955
+ async def aprocess():
902
956
  try:
903
957
  value = await send(None)
904
958
  while True:
905
959
  try:
906
- yield_type, value = await extract(value)
960
+ yield_type, value = await aextract(value)
907
961
  match yield_type:
962
+ case 0:
963
+ return
908
964
  case 1:
909
965
  yield value
910
966
  case 2:
@@ -921,7 +977,7 @@ def run_gen_step_iter(
921
977
  e = e.exception
922
978
  if isinstance(e, StopIteration):
923
979
  try:
924
- yield_type, value = await extract(e.value)
980
+ yield_type, value = await aextract(e.value)
925
981
  match yield_type:
926
982
  case 1:
927
983
  yield value
@@ -936,18 +992,21 @@ def run_gen_step_iter(
936
992
  raise
937
993
  finally:
938
994
  await close()
995
+ return aprocess()
939
996
  else:
997
+ def extract(value, /):
998
+ yield_type = -1
999
+ may_call = default_may_call
1000
+ if isinstance(value, YieldBase):
1001
+ yield_type = value.yield_type
1002
+ maybe_callable = value.may_call
1003
+ if maybe_callable is not None:
1004
+ may_call = maybe_callable
1005
+ value = value.value
1006
+ if may_call is 1 or may_call and callable(value):
1007
+ value = value()
1008
+ return yield_type, value
940
1009
  def process():
941
- def extract(value, /):
942
- try_call_me = True
943
- yield_type = -1
944
- if isinstance(value, YieldBase):
945
- try_call_me = value.try_call_me
946
- yield_type = value.yield_type
947
- value = value.value
948
- if try_call_me and callable(value):
949
- value = value()
950
- return yield_type, value
951
1010
  try:
952
1011
  value = send(None)
953
1012
  while True:
@@ -978,7 +1037,7 @@ def run_gen_step_iter(
978
1037
  throw(e)
979
1038
  finally:
980
1039
  close()
981
- return process()
1040
+ return process()
982
1041
 
983
1042
 
984
1043
  @optional
@@ -986,23 +1045,29 @@ def as_gen_step(
986
1045
  func: Callable,
987
1046
  /,
988
1047
  iter: bool = False,
1048
+ may_await: bool | Literal[1] = True,
1049
+ may_call: bool | Literal[1] = True,
989
1050
  threaded: bool = False,
990
- running_flag: None | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] | SupportsBool = None,
1051
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
991
1052
  *,
992
1053
  async_: None | bool = None,
993
1054
  ) -> Callable:
994
- if async_ is None:
995
- async_ = _get_async()
1055
+ """装饰器,构建一个 gen_step 函数
1056
+ """
996
1057
  def wrapper(*args, **kwds):
997
1058
  if iter:
998
1059
  return run_gen_step_iter(
999
1060
  func(*args, **kwds),
1061
+ may_await=may_await,
1062
+ may_call=may_call,
1000
1063
  threaded=threaded,
1001
1064
  async_=async_, # type: ignore
1002
1065
  )
1003
1066
  else:
1004
1067
  return run_gen_step(
1005
1068
  func(*args, **kwds),
1069
+ may_await=may_await,
1070
+ may_call=may_call,
1006
1071
  threaded=threaded,
1007
1072
  running_flag=running_flag,
1008
1073
  async_=async_,
@@ -1079,6 +1144,8 @@ def with_iter_next[T](
1079
1144
  /,
1080
1145
  async_: Literal[False, True] = False,
1081
1146
  ):
1147
+ """
1148
+ """
1082
1149
  if async_:
1083
1150
  get_next: Callable[[], T] | Callable[[], Awaitable[T]] = ensure_aiter(iterable).__anext__
1084
1151
  elif isinstance(iterable, Iterable):
@@ -1117,6 +1184,8 @@ def context[T](
1117
1184
  *ctxs: ContextManager | AsyncContextManager,
1118
1185
  async_: Literal[False, True] = False,
1119
1186
  ) -> T | Coroutine[Any, Any, T]:
1187
+ """
1188
+ """
1120
1189
  if async_:
1121
1190
  async def call():
1122
1191
  args: list = []
@@ -1165,6 +1234,8 @@ def backgroud_loop(
1165
1234
  *,
1166
1235
  async_: Literal[False, True] = False,
1167
1236
  ) -> ContextManager | AsyncContextManager:
1237
+ """
1238
+ """
1168
1239
  use_default_call = not callable(call)
1169
1240
  if use_default_call:
1170
1241
  start = time()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-iterutils"
3
- version = "0.1.11"
3
+ version = "0.2.1"
4
4
  description = "Python another itertools."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"