python-iterutils 0.2__tar.gz → 0.2.2__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.2
3
+ Version: 0.2.2
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,17 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 2)
5
+ __version__ = (0, 2, 2)
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_sync_iter", "run_gen_step_async_iter",
14
+ "run_gen_step_iter", "as_gen_step", "bfs_gen", "with_iter_next",
15
+ "backgroud_loop",
13
16
  ]
14
17
 
15
18
  from abc import ABC, abstractmethod
@@ -17,11 +20,14 @@ from asyncio import create_task, sleep as async_sleep, to_thread
17
20
  from builtins import map as _map, filter as _filter, zip as _zip
18
21
  from collections import defaultdict, deque
19
22
  from collections.abc import (
20
- AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable, Collection, Container,
21
- Coroutine, Generator, Iterable, Iterator, Mapping, MutableMapping, MutableSet,
22
- MutableSequence, Sequence, ValuesView,
23
+ AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable,
24
+ Collection, Container, Coroutine, Generator, Iterable, Iterator,
25
+ Mapping, MutableMapping, MutableSet, MutableSequence, Sequence,
26
+ ValuesView,
27
+ )
28
+ from contextlib import (
29
+ asynccontextmanager, contextmanager, ExitStack, AsyncExitStack,
23
30
  )
24
- from contextlib import asynccontextmanager, contextmanager, ExitStack, AsyncExitStack
25
31
  from copy import copy
26
32
  from dataclasses import dataclass
27
33
  from itertools import batched, pairwise
@@ -30,11 +36,14 @@ from sys import _getframe
30
36
  from _thread import start_new_thread
31
37
  from time import sleep, time
32
38
  from types import FrameType
33
- from typing import cast, overload, Any, AsyncContextManager, ContextManager, Literal, Protocol
39
+ from typing import (
40
+ cast, overload, Any, AsyncContextManager, ContextManager, Literal,
41
+ Protocol,
42
+ )
34
43
 
35
44
  from asynctools import (
36
- async_filter, async_map, async_reduce, async_zip, async_batched, ensure_async, ensure_aiter,
37
- collect as async_collect,
45
+ async_filter, async_map, async_reduce, async_zip, async_batched,
46
+ ensure_async, ensure_aiter, collect as async_collect,
38
47
  )
39
48
  from decotools import optional
40
49
  from texttools import format_time
@@ -42,22 +51,27 @@ from undefined import undefined
42
51
 
43
52
 
44
53
  class SupportsBool(Protocol):
54
+ """
55
+ """
45
56
  def __bool__(self, /) -> bool: ...
46
57
 
47
58
 
48
59
  class Reraised(BaseException):
49
-
60
+ """
61
+ """
50
62
  def __init__(self, exc: BaseException, /):
51
63
  if isinstance(exc, Reraised):
52
64
  exc = exc.exception
53
65
  self.exception: BaseException = exc
54
66
 
55
67
 
56
- @dataclass(slots=True, frozen=True)
68
+ @dataclass(slots=True, frozen=True, unsafe_hash=True)
57
69
  class YieldBase(ABC):
70
+ """
71
+ """
58
72
  value: Any
59
- may_await: None | bool = True
60
- may_call: None | bool = True
73
+ may_await: None | bool | Literal[1] = False
74
+ may_call: None | bool | Literal[1] = None
61
75
 
62
76
  @property
63
77
  @abstractmethod
@@ -66,18 +80,29 @@ class YieldBase(ABC):
66
80
 
67
81
 
68
82
  class Return(YieldBase):
83
+ """
84
+ """
85
+ __slots__ = ()
69
86
  yield_type = 0
70
87
 
71
88
 
72
89
  class Yield(YieldBase):
90
+ """
91
+ """
92
+ __slots__ = ()
73
93
  yield_type = 1
74
94
 
75
95
 
76
96
  class YieldFrom(YieldBase):
97
+ """
98
+ """
99
+ __slots__ = ()
77
100
  yield_type = 2
78
101
 
79
102
 
80
103
  def iterable(iterable, /) -> bool:
104
+ """
105
+ """
81
106
  try:
82
107
  return isinstance(iter(iterable), Iterable)
83
108
  except TypeError:
@@ -85,6 +110,8 @@ def iterable(iterable, /) -> bool:
85
110
 
86
111
 
87
112
  def async_iterable(iterable, /) -> bool:
113
+ """
114
+ """
88
115
  try:
89
116
  return isinstance(iter(iterable), AsyncIterable)
90
117
  except TypeError:
@@ -97,6 +124,8 @@ def foreach(
97
124
  /,
98
125
  *iterables: Iterable | AsyncIterable,
99
126
  ):
127
+ """
128
+ """
100
129
  if not (isinstance(iterable, Iterable) and all(isinstance(it, Iterable) for it in iterables)):
101
130
  return async_foreach(value, iterable, *iterables)
102
131
  if iterables:
@@ -114,6 +143,8 @@ async def async_foreach(
114
143
  *iterables: Iterable | AsyncIterable,
115
144
  threaded: bool = False,
116
145
  ):
146
+ """
147
+ """
117
148
  value = ensure_async(value, threaded=threaded)
118
149
  if iterables:
119
150
  async for args in async_zip(iterable, *iterables, threaded=threaded):
@@ -128,6 +159,8 @@ def through(
128
159
  /,
129
160
  take_while: None | Callable = None,
130
161
  ):
162
+ """
163
+ """
131
164
  if not isinstance(iterable, Iterable):
132
165
  return async_through(iterable, take_while)
133
166
  if take_while is None:
@@ -145,6 +178,8 @@ async def async_through(
145
178
  take_while: None | Callable = None,
146
179
  threaded: bool = False,
147
180
  ):
181
+ """
182
+ """
148
183
  iterable = ensure_aiter(iterable, threaded=threaded)
149
184
  if take_while is None:
150
185
  async for _ in iterable:
@@ -178,6 +213,8 @@ def flatten(
178
213
  /,
179
214
  exclude_types: type | tuple[type, ...] = (Buffer, str),
180
215
  ) -> Iterator | AsyncIterator:
216
+ """
217
+ """
181
218
  if not isinstance(iterable, Iterable):
182
219
  return async_flatten(iterable, exclude_types)
183
220
  def gen(iterable):
@@ -195,6 +232,8 @@ async def async_flatten(
195
232
  exclude_types: type | tuple[type, ...] = (Buffer, str),
196
233
  threaded: bool = False,
197
234
  ) -> AsyncIterator:
235
+ """
236
+ """
198
237
  async for e in ensure_aiter(iterable, threaded=threaded):
199
238
  if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
200
239
  async for e in async_flatten(e, exclude_types, threaded=threaded):
@@ -236,6 +275,8 @@ def collect(
236
275
  /,
237
276
  rettype: Callable[[Iterable], Collection] = list,
238
277
  ) -> Collection | Coroutine[Any, Any, Collection]:
278
+ """
279
+ """
239
280
  if not isinstance(iterable, Iterable):
240
281
  return async_collect(iterable, rettype)
241
282
  return rettype(iterable)
@@ -274,6 +315,8 @@ def group_collect[K, V, C: Container, M: MutableMapping](
274
315
  mapping: None | M = None,
275
316
  factory: None | C | Callable[[], C] = None,
276
317
  ) -> dict[K, C] | M | Coroutine[Any, Any, dict[K, C]] | Coroutine[Any, Any, M]:
318
+ """
319
+ """
277
320
  if not isinstance(iterable, Iterable):
278
321
  return async_group_collect(iterable, mapping, factory)
279
322
  if factory is None:
@@ -331,6 +374,8 @@ async def async_group_collect[K, V, C: Container, M: MutableMapping](
331
374
  factory: None | C | Callable[[], C] = None,
332
375
  threaded: bool = False,
333
376
  ) -> dict[K, C] | M:
377
+ """
378
+ """
334
379
  iterable = ensure_aiter(iterable, threaded=threaded)
335
380
  if factory is None:
336
381
  if isinstance(mapping, defaultdict):
@@ -370,7 +415,9 @@ def map(
370
415
  iterable: Iterable | AsyncIterable,
371
416
  /,
372
417
  *iterables: Iterable | AsyncIterable,
373
- ):
418
+ ):
419
+ """
420
+ """
374
421
  if (
375
422
  iscoroutinefunction(function) or
376
423
  isinstance(iterable, AsyncIterable) or
@@ -395,6 +442,8 @@ def filter(
395
442
  iterable: Iterable | AsyncIterable,
396
443
  /,
397
444
  ):
445
+ """
446
+ """
398
447
  if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
399
448
  return async_filter(function, iterable)
400
449
  return _filter(function, iterable)
@@ -406,6 +455,8 @@ def reduce(
406
455
  initial: Any = undefined,
407
456
  /,
408
457
  ):
458
+ """
459
+ """
409
460
  if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
410
461
  return async_reduce(function, iterable, initial)
411
462
  from functools import reduce
@@ -419,6 +470,8 @@ def zip(
419
470
  /,
420
471
  *iterables: Iterable | AsyncIterable,
421
472
  ):
473
+ """
474
+ """
422
475
  if isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
423
476
  return async_zip(iterable, *iterables)
424
477
  return _zip(iterable, *iterables)
@@ -443,6 +496,8 @@ def chunked[T](
443
496
  n: int = 1,
444
497
  /,
445
498
  ) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
499
+ """
500
+ """
446
501
  if n < 0:
447
502
  n = 1
448
503
  if isinstance(iterable, Sequence):
@@ -474,6 +529,8 @@ def iter_unique[T](
474
529
  /,
475
530
  seen: None | MutableSet = None,
476
531
  ) -> Iterator[T] | AsyncIterator[T]:
532
+ """
533
+ """
477
534
  if not isinstance(iterable, Iterable):
478
535
  return async_iter_unique(iterable, seen)
479
536
  if seen is None:
@@ -493,6 +550,8 @@ async def async_iter_unique[T](
493
550
  seen: None | MutableSet = None,
494
551
  threaded: bool = False,
495
552
  ) -> AsyncIterator[T]:
553
+ """
554
+ """
496
555
  if seen is None:
497
556
  seen = set()
498
557
  add = seen.add
@@ -508,8 +567,6 @@ def wrap_iter[T](
508
567
  /,
509
568
  callprev: None | Callable[[T], Any] = None,
510
569
  callnext: None | Callable[[T], Any] = None,
511
- callenter: None | Callable[[Iterable[T]], Any] = None,
512
- callexit: None | Callable[[Iterable[T], None | BaseException], Any] = None,
513
570
  ) -> Iterator[T]:
514
571
  ...
515
572
  @overload
@@ -518,8 +575,6 @@ def wrap_iter[T](
518
575
  /,
519
576
  callprev: None | Callable[[T], Any] = None,
520
577
  callnext: None | Callable[[T], Any] = None,
521
- callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
522
- callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
523
578
  ) -> AsyncIterator[T]:
524
579
  ...
525
580
  def wrap_iter[T](
@@ -527,47 +582,24 @@ def wrap_iter[T](
527
582
  /,
528
583
  callprev: None | Callable[[T], Any] = None,
529
584
  callnext: None | Callable[[T], Any] = None,
530
- callenter: None | Callable[[Iterable[T]], Any] | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
531
- callexit: ( None | Callable[[Iterable[T], None | BaseException], Any] |
532
- Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] ) = None,
533
585
  ) -> Iterator[T] | AsyncIterator[T]:
586
+ """
587
+ """
534
588
  if not isinstance(iterable, Iterable):
535
589
  return wrap_aiter(
536
590
  iterable,
537
591
  callprev=callprev,
538
592
  callnext=callnext,
539
- callenter=callenter, # type: ignore
540
- callexit=callexit, # type: ignore
541
593
  )
542
594
  if not callable(callprev):
543
595
  callprev = None
544
596
  if not callable(callnext):
545
597
  callnext = None
546
598
  def gen():
547
- try:
548
- if callable(callenter):
549
- callenter(iterable)
550
- for e in iterable:
551
- if callprev:
552
- try:
553
- callprev(e)
554
- except (StopIteration, GeneratorExit):
555
- break
556
- yield e
557
- if callnext:
558
- try:
559
- callnext(e)
560
- except (StopIteration, GeneratorExit):
561
- break
562
- except BaseException as e:
563
- if callable(callexit):
564
- if not callexit(iterable, e):
565
- raise
566
- else:
567
- raise
568
- finally:
569
- if callable(callexit):
570
- callexit(iterable, None)
599
+ for e in iterable:
600
+ callprev and callprev(e)
601
+ yield e
602
+ callnext and callnext(e)
571
603
  return gen()
572
604
 
573
605
 
@@ -576,34 +608,16 @@ async def wrap_aiter[T](
576
608
  /,
577
609
  callprev: None | Callable[[T], Any] = None,
578
610
  callnext: None | Callable[[T], Any] = None,
579
- callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
580
- callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
581
611
  threaded: bool = False,
582
612
  ) -> AsyncIterator[T]:
613
+ """
614
+ """
583
615
  callprev = ensure_async(callprev, threaded=threaded) if callable(callprev) else None
584
616
  callnext = ensure_async(callnext, threaded=threaded) if callable(callnext) else None
585
- try:
586
- async for e in ensure_aiter(iterable, threaded=threaded):
587
- if callprev:
588
- try:
589
- await callprev(e)
590
- except (StopAsyncIteration, GeneratorExit):
591
- break
592
- yield e
593
- if callnext:
594
- try:
595
- await callnext(e)
596
- except (StopAsyncIteration, GeneratorExit):
597
- break
598
- except BaseException as e:
599
- if callable(callexit):
600
- if not await ensure_async(callexit, threaded=threaded)(iterable, e):
601
- raise
602
- else:
603
- raise
604
- finally:
605
- if callable(callexit):
606
- await ensure_async(callexit, threaded=threaded)(iterable, None)
617
+ async for e in ensure_aiter(iterable, threaded=threaded):
618
+ callprev and await callprev(e)
619
+ yield e
620
+ callnext and await callnext(e)
607
621
 
608
622
 
609
623
  def acc_step(
@@ -611,6 +625,8 @@ def acc_step(
611
625
  stop: None | int = None,
612
626
  step: int = 1,
613
627
  ) -> Iterator[tuple[int, int, int]]:
628
+ """
629
+ """
614
630
  if stop is None:
615
631
  start, stop = 0, start
616
632
  for i in range(start + step, stop, step):
@@ -624,6 +640,8 @@ def cut_iter(
624
640
  stop: None | int = None,
625
641
  step: int = 1,
626
642
  ) -> Iterator[tuple[int, int]]:
643
+ """
644
+ """
627
645
  if stop is None:
628
646
  start, stop = 0, start
629
647
  for start in range(start + step, stop, step):
@@ -639,7 +657,7 @@ def _get_async(back: int = 2) -> bool:
639
657
  f_locals = f.f_locals
640
658
  if f_locals is f_globals:
641
659
  return f_locals.get("async_") or False
642
- while f_locals is not f_globals:
660
+ while f_locals is not None and f_locals is not f_globals:
643
661
  if "async_" in f_locals:
644
662
  return f_locals["async_"] or False
645
663
  f = f.f_back
@@ -652,23 +670,25 @@ def _get_async(back: int = 2) -> bool:
652
670
  @overload
653
671
  def call_as_async[**Args, T: Coroutine](
654
672
  func: Callable[Args, T], /,
655
- may_await: None | bool = False,
673
+ may_await: bool | Literal[1] = False,
656
674
  threaded: bool = False,
657
675
  ) -> Callable[Args, T]:
658
676
  ...
659
677
  @overload
660
678
  def call_as_async[**Args, T](
661
679
  func: Callable[Args, T], /,
662
- may_await: None | bool = False,
680
+ may_await: bool | Literal[1] = False,
663
681
  threaded: bool = False,
664
682
  ) -> Callable[Args, Coroutine[Any, Any, T]]:
665
683
  ...
666
684
  def call_as_async[**Args, T](
667
685
  func: Callable[Args, T],
668
686
  /,
669
- may_await: None | bool = False,
687
+ may_await: bool | Literal[1] = False,
670
688
  threaded: bool = False,
671
689
  ) -> Callable[Args, T] | Callable[Args, Coroutine[Any, Any, T]]:
690
+ """
691
+ """
672
692
  if iscoroutinefunction(func):
673
693
  return func
674
694
  def wraps(*args, **kwds):
@@ -681,7 +701,7 @@ def call_as_async[**Args, T](
681
701
  value = await to_thread(wraps, *args, **kwds)
682
702
  else:
683
703
  value = wraps(*args, **kwds)
684
- if may_await is None or may_await and isawaitable(value):
704
+ if may_await is 1 or may_await and isawaitable(value):
685
705
  value = await value
686
706
  return value
687
707
  return wrapper
@@ -689,140 +709,131 @@ def call_as_async[**Args, T](
689
709
 
690
710
  def iter_gen_step(
691
711
  gen_step: Generator | Callable[[], Generator],
692
- simple: bool = False,
712
+ may_call: bool | Literal[1] = True,
693
713
  ):
694
- gen = gen_step() if callable(gen_step) else gen_step
695
- send = gen.send
696
- throw = gen.throw
697
- close = gen.close
714
+ """
715
+ """
716
+ if callable(gen_step):
717
+ gen_step = gen_step()
718
+ send = gen_step.send
719
+ throw = gen_step.throw
720
+ close = gen_step.close
698
721
  value: Any = None
699
- if simple:
700
- try:
701
- while True:
702
- yield (value := send(value))
703
- except StopIteration as e:
704
- yield e.value
705
- finally:
706
- close()
707
- else:
708
- try:
709
- while True:
710
- if isinstance(value, YieldBase):
711
- raise StopIteration(value)
712
- try:
713
- if callable(value):
714
- value = value()
715
- except BaseException as e:
716
- value = throw(e)
717
- else:
718
- value = send(value)
719
- yield value
720
- except StopIteration as e:
721
- may_call: None | bool = True
722
- value = e.value
722
+ try:
723
+ while True:
723
724
  if isinstance(value, YieldBase):
724
- may_call = value.may_call
725
- value = value.value
726
- if may_call is None or may_call and callable(value):
727
- try:
725
+ raise StopIteration(value)
726
+ try:
727
+ if may_call is 1 or may_call and callable(value):
728
728
  value = value()
729
- except BaseException as e:
730
- try:
731
- value = throw(e)
732
- except BaseException as e:
733
- raise Reraised(e) from e
729
+ except BaseException as e:
730
+ value = throw(e)
731
+ else:
732
+ value = send(value)
734
733
  yield value
735
- finally:
736
- close()
734
+ except StopIteration as e:
735
+ value = e.value
736
+ if isinstance(value, YieldBase):
737
+ maybe_callable = value.may_call
738
+ if maybe_callable is not None:
739
+ may_call = maybe_callable
740
+ value = value.value
741
+ if may_call is 1 or may_call and callable(value):
742
+ try:
743
+ value = value()
744
+ except BaseException as e:
745
+ try:
746
+ value = throw(e)
747
+ except BaseException as e:
748
+ raise Reraised(e) from e
749
+ yield value
750
+ finally:
751
+ close()
737
752
 
738
753
 
739
754
  async def iter_gen_step_async(
740
755
  gen_step: Generator | Callable[[], Generator],
756
+ may_await: bool | Literal[1] = True,
757
+ may_call: bool | Literal[1] = True,
741
758
  threaded: bool = False,
742
- simple: bool = False,
743
759
  ):
744
- gen = gen_step() if callable(gen_step) else gen_step
745
- send: Callable = gen.send
746
- throw: Callable = gen.throw
747
- close: Callable = call_as_async(gen.close, threaded=threaded)
760
+ """
761
+ """
762
+ if callable(gen_step):
763
+ gen_step = gen_step()
764
+ send: Callable = call_as_async(gen_step.send, threaded=threaded)
765
+ throw: Callable = call_as_async(gen_step.throw, threaded=threaded)
766
+ close: Callable = call_as_async(gen_step.close, threaded=threaded)
748
767
  value: Any = None
749
- if simple:
750
- send = call_as_async(send, may_await=None, threaded=threaded)
751
- throw = call_as_async(throw, may_await=None, threaded=threaded)
752
- try:
753
- while True:
754
- yield (value := await send(value))
755
- except BaseException as e:
756
- if isinstance(e, Reraised):
757
- e = e.exception
758
- if isinstance(e, StopIteration):
759
- yield e.value
768
+ try:
769
+ while True:
770
+ if isinstance(value, YieldBase):
771
+ raise StopIteration(value)
772
+ try:
773
+ if may_call is 1 or may_call and callable(value):
774
+ value = await call_as_async(
775
+ value, may_await=may_await, threaded=threaded)()
776
+ elif may_await is 1 or may_await and isawaitable(value):
777
+ value = await value
778
+ except BaseException as e:
779
+ if isinstance(e, Reraised):
780
+ e = e.exception
781
+ value = await throw(e)
760
782
  else:
761
- raise
762
- finally:
763
- await close()
764
- else:
765
- send = call_as_async(send, threaded=threaded)
766
- throw = call_as_async(throw, threaded=threaded)
767
- try:
768
- while True:
769
- if isinstance(value, YieldBase):
770
- raise StopIteration(value)
783
+ value = await send(value)
784
+ yield value
785
+ except BaseException as e:
786
+ if isinstance(e, Reraised):
787
+ e = e.exception
788
+ if isinstance(e, StopIteration):
789
+ value = e.value
790
+ if isinstance(value, YieldBase):
791
+ maybe_awaitable = value.may_await
792
+ if maybe_awaitable is not None:
793
+ may_await = maybe_awaitable
794
+ maybe_callable = value.may_call
795
+ if maybe_callable is not None:
796
+ may_call = maybe_callable
797
+ value = value.value
798
+ try:
799
+ if may_call is 1 or may_call and callable(value):
800
+ value = await call_as_async(
801
+ value, may_await=may_await, threaded=threaded)()
802
+ elif may_await is 1 or may_await and isawaitable(value):
803
+ value = await value
804
+ except BaseException as e:
771
805
  try:
772
- if callable(value):
773
- value = await call_as_async(
774
- value, may_await=True, threaded=threaded)()
775
- elif isawaitable(value):
776
- value = await value
777
- except BaseException as e:
778
- if isinstance(e, Reraised):
779
- e = e.exception
780
806
  value = await throw(e)
781
- else:
782
- value = await send(value)
783
- yield value
784
- except BaseException as e:
785
- if isinstance(e, Reraised):
786
- e = e.exception
787
- if isinstance(e, StopIteration):
788
- may_await: None | bool = True
789
- may_call: None | bool = True
790
- value = e.value
791
- if isinstance(value, YieldBase):
792
- may_await = value.may_await
793
- may_call = value.may_call
794
- value = value.value
795
- try:
796
- if may_call is None or may_call and callable(value):
797
- value = await call_as_async(
798
- value, may_await=may_await, threaded=threaded)()
799
- elif may_await is None or may_await and isawaitable(value):
800
- value = await value
801
807
  except BaseException as e:
802
- try:
803
- value = await throw(e)
804
- except BaseException as e:
805
- raise Reraised(e) from e
806
- yield value
807
- else:
808
- raise
809
- finally:
810
- await close()
808
+ raise Reraised(e) from e
809
+ yield value
810
+ else:
811
+ raise
812
+ finally:
813
+ await close()
811
814
 
812
815
 
813
816
  def run_gen_step(
814
817
  gen_step: Generator | Callable[[], Generator],
815
- simple: bool = False,
818
+ may_await: bool | Literal[1] = True,
819
+ may_call: bool | Literal[1] = True,
816
820
  threaded: bool = False,
817
821
  running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
818
822
  *,
819
823
  async_: None | bool = None,
820
824
  ):
825
+ """
826
+ """
821
827
  if async_ is None:
822
828
  async_ = _get_async()
823
829
  if async_:
824
830
  async def process():
825
- gen = iter_gen_step_async(gen_step, simple=simple, threaded=threaded)
831
+ gen = iter_gen_step_async(
832
+ gen_step,
833
+ may_await=may_await,
834
+ may_call=may_call,
835
+ threaded=threaded,
836
+ )
826
837
  try:
827
838
  if running_flag is None:
828
839
  async for value in gen:
@@ -849,7 +860,7 @@ def run_gen_step(
849
860
  raise
850
861
  return process()
851
862
  else:
852
- gen = iter_gen_step(gen_step, simple=simple)
863
+ gen = iter_gen_step(gen_step, may_call=may_call)
853
864
  try:
854
865
  if running_flag is None:
855
866
  for value in gen:
@@ -871,10 +882,136 @@ def run_gen_step(
871
882
  raise
872
883
 
873
884
 
885
+ def run_gen_step_sync_iter(
886
+ gen_step: Generator | Callable[[], Generator],
887
+ may_call: bool | Literal[1] = True,
888
+ ) -> Iterator:
889
+ """
890
+ """
891
+ if callable(gen_step):
892
+ gen_step = gen_step()
893
+ send: Callable = gen_step.send
894
+ throw: Callable = gen_step.throw
895
+ close: Callable = gen_step.close
896
+ def extract(value, may_call=may_call, /):
897
+ yield_type = -1
898
+ if isinstance(value, YieldBase):
899
+ yield_type = value.yield_type
900
+ maybe_callable = value.may_call
901
+ if maybe_callable is not None:
902
+ may_call = maybe_callable
903
+ value = value.value
904
+ if may_call is 1 or may_call and callable(value):
905
+ value = value()
906
+ return yield_type, value
907
+ try:
908
+ value = send(None)
909
+ while True:
910
+ try:
911
+ yield_type, value = extract(value)
912
+ match yield_type:
913
+ case 0:
914
+ return value
915
+ case 1:
916
+ yield value
917
+ case 2:
918
+ yield from value
919
+ except BaseException as e:
920
+ value = throw(e)
921
+ else:
922
+ value = send(value)
923
+ except StopIteration as e:
924
+ try:
925
+ yield_type, value = extract(e.value)
926
+ match yield_type:
927
+ case 1:
928
+ yield value
929
+ case 2:
930
+ yield from value
931
+ case _:
932
+ return value
933
+ except BaseException as e:
934
+ throw(e)
935
+ finally:
936
+ close()
937
+
938
+
939
+ async def run_gen_step_async_iter(
940
+ gen_step: Generator | Callable[[], Generator],
941
+ may_await: bool | Literal[1] = True,
942
+ may_call: bool | Literal[1] = True,
943
+ threaded: bool = False,
944
+ ) -> AsyncIterator:
945
+ """
946
+ """
947
+ if callable(gen_step):
948
+ gen_step = gen_step()
949
+ send: Callable = call_as_async(gen_step.send, threaded=threaded)
950
+ throw: Callable = call_as_async(gen_step.throw, threaded=threaded)
951
+ close: Callable = call_as_async(gen_step.close, threaded=threaded)
952
+ async def extract(value, may_await=may_await, may_call=may_call, /):
953
+ yield_type = -1
954
+ if isinstance(value, YieldBase):
955
+ yield_type = value.yield_type
956
+ maybe_awaitable = value.may_await
957
+ if maybe_awaitable is not None:
958
+ may_await = maybe_awaitable
959
+ maybe_callable = value.may_call
960
+ if maybe_callable is not None:
961
+ may_call = maybe_callable
962
+ value = value.value
963
+ if may_call is 1 or may_call and callable(value):
964
+ value = await call_as_async(
965
+ value, may_await=may_await, threaded=threaded)()
966
+ elif may_await is 1 or may_await and isawaitable(value):
967
+ value = await value
968
+ return yield_type, value
969
+ try:
970
+ value = await send(None)
971
+ while True:
972
+ try:
973
+ yield_type, value = await extract(value)
974
+ match yield_type:
975
+ case 0:
976
+ return
977
+ case 1:
978
+ yield value
979
+ case 2:
980
+ async for val in ensure_aiter(value, threaded=threaded):
981
+ yield val
982
+ except BaseException as e:
983
+ if isinstance(e, Reraised):
984
+ e = e.exception
985
+ value = await throw(e)
986
+ else:
987
+ value = await send(value)
988
+ except BaseException as e:
989
+ if isinstance(e, Reraised):
990
+ e = e.exception
991
+ if isinstance(e, StopIteration):
992
+ try:
993
+ yield_type, value = await extract(e.value)
994
+ match yield_type:
995
+ case 1:
996
+ yield value
997
+ case 2:
998
+ async for val in ensure_aiter(value, threaded=threaded):
999
+ yield val
1000
+ except BaseException as e:
1001
+ if isinstance(e, Reraised):
1002
+ e = e.exception
1003
+ await throw(e)
1004
+ else:
1005
+ raise
1006
+ finally:
1007
+ await close()
1008
+
1009
+
874
1010
  @overload
875
1011
  def run_gen_step_iter(
876
1012
  gen_step: Generator | Callable[[], Generator],
877
- simple: bool = False,
1013
+ may_await: bool | Literal[1] = True,
1014
+ may_call: bool | Literal[1] = True,
878
1015
  threaded: bool = False,
879
1016
  *,
880
1017
  async_: None = None,
@@ -883,7 +1020,8 @@ def run_gen_step_iter(
883
1020
  @overload
884
1021
  def run_gen_step_iter(
885
1022
  gen_step: Generator | Callable[[], Generator],
886
- simple: bool = False,
1023
+ may_await: bool | Literal[1] = True,
1024
+ may_call: bool | Literal[1] = True,
887
1025
  threaded: bool = False,
888
1026
  *,
889
1027
  async_: Literal[False],
@@ -892,7 +1030,8 @@ def run_gen_step_iter(
892
1030
  @overload
893
1031
  def run_gen_step_iter(
894
1032
  gen_step: Generator | Callable[[], Generator],
895
- simple: bool = False,
1033
+ may_await: bool | Literal[1] = True,
1034
+ may_call: bool | Literal[1] = True,
896
1035
  threaded: bool = False,
897
1036
  *,
898
1037
  async_: Literal[True],
@@ -900,120 +1039,25 @@ def run_gen_step_iter(
900
1039
  ...
901
1040
  def run_gen_step_iter(
902
1041
  gen_step: Generator | Callable[[], Generator],
903
- simple: bool = False,
1042
+ may_await: bool | Literal[1] = True,
1043
+ may_call: bool | Literal[1] = True,
904
1044
  threaded: bool = False,
905
1045
  *,
906
1046
  async_: None | bool = None,
907
1047
  ) -> Iterator | AsyncIterator:
1048
+ """
1049
+ """
908
1050
  if async_ is None:
909
1051
  async_ = _get_async()
910
- gen = gen_step() if callable(gen_step) else gen_step
911
- send: Callable = gen.send
912
- throw: Callable = gen.throw
913
- close: Callable = gen.close
914
1052
  if async_:
915
- send = call_as_async(send, threaded=threaded)
916
- throw = call_as_async(throw, threaded=threaded)
917
- close = call_as_async(close, threaded=threaded)
918
- async def aextract(value, /):
919
- may_await: None | bool = None if simple else False
920
- may_call: None | bool = not simple
921
- yield_type = -1
922
- if isinstance(value, YieldBase):
923
- yield_type = value.yield_type
924
- may_await = value.may_await
925
- may_call = value.may_call
926
- value = value.value
927
- if may_call is None or may_call and callable(value):
928
- value = await call_as_async(
929
- value, may_await=may_await, threaded=threaded)()
930
- elif may_await is None or may_await and isawaitable(value):
931
- value = await value
932
- return yield_type, value
933
- async def aprocess():
934
- try:
935
- value = await send(None)
936
- while True:
937
- try:
938
- yield_type, value = await aextract(value)
939
- match yield_type:
940
- case 0:
941
- return
942
- case 1:
943
- yield value
944
- case 2:
945
- async for val in ensure_aiter(value, threaded=threaded):
946
- yield val
947
- except BaseException as e:
948
- if isinstance(e, Reraised):
949
- e = e.exception
950
- value = await throw(e)
951
- else:
952
- value = await send(value)
953
- except BaseException as e:
954
- if isinstance(e, Reraised):
955
- e = e.exception
956
- if isinstance(e, StopIteration):
957
- try:
958
- yield_type, value = await aextract(e.value)
959
- match yield_type:
960
- case 1:
961
- yield value
962
- case 2:
963
- async for val in ensure_aiter(value, threaded=threaded):
964
- yield val
965
- except BaseException as e:
966
- if isinstance(e, Reraised):
967
- e = e.exception
968
- await throw(e)
969
- else:
970
- raise
971
- finally:
972
- await close()
973
- return aprocess()
1053
+ return run_gen_step_async_iter(
1054
+ gen_step,
1055
+ may_await=may_await,
1056
+ may_call=may_call,
1057
+ threaded=threaded,
1058
+ )
974
1059
  else:
975
- def extract(value, /):
976
- may_call: None | bool = not simple
977
- yield_type = -1
978
- if isinstance(value, YieldBase):
979
- yield_type = value.yield_type
980
- may_call = value.may_call
981
- value = value.value
982
- if may_call is None or may_call and callable(value):
983
- value = value()
984
- return yield_type, value
985
- def process():
986
- try:
987
- value = send(None)
988
- while True:
989
- try:
990
- yield_type, value = extract(value)
991
- match yield_type:
992
- case 0:
993
- return value
994
- case 1:
995
- yield value
996
- case 2:
997
- yield from value
998
- except BaseException as e:
999
- value = throw(e)
1000
- else:
1001
- value = send(value)
1002
- except StopIteration as e:
1003
- try:
1004
- yield_type, value = extract(e.value)
1005
- match yield_type:
1006
- case 1:
1007
- yield value
1008
- case 2:
1009
- yield from value
1010
- case _:
1011
- return value
1012
- except BaseException as e:
1013
- throw(e)
1014
- finally:
1015
- close()
1016
- return process()
1060
+ return run_gen_step_sync_iter(gen_step, may_call=may_call)
1017
1061
 
1018
1062
 
1019
1063
  @optional
@@ -1021,7 +1065,8 @@ def as_gen_step(
1021
1065
  func: Callable,
1022
1066
  /,
1023
1067
  iter: bool = False,
1024
- simple: bool = False,
1068
+ may_await: bool | Literal[1] = True,
1069
+ may_call: bool | Literal[1] = True,
1025
1070
  threaded: bool = False,
1026
1071
  running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
1027
1072
  *,
@@ -1029,18 +1074,22 @@ def as_gen_step(
1029
1074
  ) -> Callable:
1030
1075
  """装饰器,构建一个 gen_step 函数
1031
1076
  """
1077
+ if async_ is None:
1078
+ async_ = _get_async()
1032
1079
  def wrapper(*args, **kwds):
1033
1080
  if iter:
1034
1081
  return run_gen_step_iter(
1035
1082
  func(*args, **kwds),
1036
- simple=simple,
1083
+ may_await=may_await,
1084
+ may_call=may_call,
1037
1085
  threaded=threaded,
1038
1086
  async_=async_, # type: ignore
1039
1087
  )
1040
1088
  else:
1041
1089
  return run_gen_step(
1042
1090
  func(*args, **kwds),
1043
- simple=simple,
1091
+ may_await=may_await,
1092
+ may_call=may_call,
1044
1093
  threaded=threaded,
1045
1094
  running_flag=running_flag,
1046
1095
  async_=async_,
@@ -1094,29 +1143,36 @@ def bfs_gen[T](
1094
1143
  def with_iter_next[T](
1095
1144
  iterable: Iterable[T],
1096
1145
  /,
1097
- async_: Literal[False] = False,
1146
+ async_: Literal[False],
1098
1147
  ) -> ContextManager[Callable[[], T]]:
1099
1148
  ...
1100
1149
  @overload
1101
1150
  def with_iter_next[T](
1102
- iterable: AsyncIterable[T],
1151
+ iterable: Iterable[T] | AsyncIterable[T],
1103
1152
  /,
1104
- async_: Literal[False] = False,
1153
+ async_: Literal[True],
1105
1154
  ) -> ContextManager[Callable[[], Awaitable[T]]]:
1106
1155
  ...
1107
1156
  @overload
1108
1157
  def with_iter_next[T](
1109
1158
  iterable: Iterable[T] | AsyncIterable[T],
1110
1159
  /,
1111
- async_: Literal[True],
1112
- ) -> ContextManager[Callable[[], Awaitable[T]]]:
1160
+ async_: None = None,
1161
+ ) -> ContextManager[Callable[[], T]] | ContextManager[Callable[[], Awaitable[T]]]:
1113
1162
  ...
1114
1163
  @contextmanager
1115
1164
  def with_iter_next[T](
1116
1165
  iterable: Iterable[T] | AsyncIterable[T],
1117
1166
  /,
1118
- async_: Literal[False, True] = False,
1167
+ async_: None | Literal[False, True] = None,
1119
1168
  ):
1169
+ """
1170
+ """
1171
+ if async_ is None:
1172
+ if not isinstance(iterable, Iterable):
1173
+ async_ = True
1174
+ else:
1175
+ async_ = _get_async()
1120
1176
  if async_:
1121
1177
  get_next: Callable[[], T] | Callable[[], Awaitable[T]] = ensure_aiter(iterable).__anext__
1122
1178
  elif isinstance(iterable, Iterable):
@@ -1140,7 +1196,7 @@ def with_iter_next[T](
1140
1196
  def context[T](
1141
1197
  func: Callable[..., T],
1142
1198
  *ctxs: ContextManager,
1143
- async_: Literal[False] = False,
1199
+ async_: Literal[False],
1144
1200
  ) -> T:
1145
1201
  ...
1146
1202
  @overload
@@ -1150,11 +1206,25 @@ def context[T](
1150
1206
  async_: Literal[True],
1151
1207
  ) -> Coroutine[Any, Any, T]:
1152
1208
  ...
1209
+ @overload
1210
+ def context[T](
1211
+ func: Callable[..., T] | Callable[..., Awaitable[T]],
1212
+ *ctxs: ContextManager | AsyncContextManager,
1213
+ async_: None = None,
1214
+ ) -> T | Coroutine[Any, Any, T]:
1215
+ ...
1153
1216
  def context[T](
1154
1217
  func: Callable[..., T] | Callable[..., Awaitable[T]],
1155
1218
  *ctxs: ContextManager | AsyncContextManager,
1156
- async_: Literal[False, True] = False,
1219
+ async_: None | Literal[False, True] = None,
1157
1220
  ) -> T | Coroutine[Any, Any, T]:
1221
+ """
1222
+ """
1223
+ if async_ is None:
1224
+ if iscoroutinefunction(func):
1225
+ async_ = True
1226
+ else:
1227
+ async_ = _get_async()
1158
1228
  if async_:
1159
1229
  async def call():
1160
1230
  args: list = []
@@ -1184,7 +1254,7 @@ def backgroud_loop(
1184
1254
  /,
1185
1255
  interval: int | float = 0.05,
1186
1256
  *,
1187
- async_: Literal[False] = False,
1257
+ async_: Literal[False],
1188
1258
  ) -> ContextManager:
1189
1259
  ...
1190
1260
  @overload
@@ -1196,13 +1266,29 @@ def backgroud_loop(
1196
1266
  async_: Literal[True],
1197
1267
  ) -> AsyncContextManager:
1198
1268
  ...
1269
+ @overload
1270
+ def backgroud_loop(
1271
+ call: None | Callable = None,
1272
+ /,
1273
+ interval: int | float = 0.05,
1274
+ *,
1275
+ async_: None = None,
1276
+ ) -> ContextManager | AsyncContextManager:
1277
+ ...
1199
1278
  def backgroud_loop(
1200
1279
  call: None | Callable = None,
1201
1280
  /,
1202
1281
  interval: int | float = 0.05,
1203
1282
  *,
1204
- async_: Literal[False, True] = False,
1283
+ async_: None | Literal[False, True] = None,
1205
1284
  ) -> ContextManager | AsyncContextManager:
1285
+ """
1286
+ """
1287
+ if async_ is None:
1288
+ if iscoroutinefunction(call):
1289
+ async_ = True
1290
+ else:
1291
+ async_ = _get_async()
1206
1292
  use_default_call = not callable(call)
1207
1293
  if use_default_call:
1208
1294
  start = time()
@@ -1221,6 +1307,7 @@ def backgroud_loop(
1221
1307
  sleep(interval)
1222
1308
  running = True
1223
1309
  if async_:
1310
+ @asynccontextmanager
1224
1311
  async def actx():
1225
1312
  nonlocal running
1226
1313
  try:
@@ -1231,8 +1318,9 @@ def backgroud_loop(
1231
1318
  task.cancel()
1232
1319
  if use_default_call:
1233
1320
  print("\r\x1b[K", end="")
1234
- return asynccontextmanager(actx)()
1321
+ return actx()
1235
1322
  else:
1323
+ @contextmanager
1236
1324
  def ctx():
1237
1325
  nonlocal running
1238
1326
  try:
@@ -1241,5 +1329,5 @@ def backgroud_loop(
1241
1329
  running = False
1242
1330
  if use_default_call:
1243
1331
  print("\r\x1b[K", end="")
1244
- return contextmanager(ctx)()
1332
+ return ctx()
1245
1333
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-iterutils"
3
- version = "0.2"
3
+ version = "0.2.2"
4
4
  description = "Python another itertools."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
File without changes