python-iterutils 0.1.9__tar.gz → 0.1.11__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.
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/PKG-INFO +1 -1
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/iterutils/__init__.py +161 -74
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/pyproject.toml +1 -1
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/LICENSE +0 -0
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/iterutils/py.typed +0 -0
- {python_iterutils-0.1.9 → python_iterutils-0.1.11}/readme.md +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 1,
|
|
5
|
+
__version__ = (0, 1, 11)
|
|
6
6
|
__all__ = [
|
|
7
7
|
"Return", "Yield", "YieldFrom", "iterable", "async_iterable", "foreach", "async_foreach",
|
|
8
8
|
"through", "async_through", "flatten", "async_flatten", "collect", "async_collect",
|
|
@@ -30,7 +30,7 @@ from sys import _getframe
|
|
|
30
30
|
from _thread import start_new_thread
|
|
31
31
|
from time import sleep, time
|
|
32
32
|
from types import FrameType
|
|
33
|
-
from typing import cast, overload, Any, AsyncContextManager, ContextManager, Literal
|
|
33
|
+
from typing import cast, overload, Any, AsyncContextManager, ContextManager, Literal, Protocol
|
|
34
34
|
|
|
35
35
|
from asynctools import (
|
|
36
36
|
async_filter, async_map, async_reduce, async_zip, async_batched, ensure_async, ensure_aiter,
|
|
@@ -41,6 +41,10 @@ from texttools import format_time
|
|
|
41
41
|
from undefined import undefined, Undefined
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
class SupportsBool(Protocol):
|
|
45
|
+
def __bool__(self, /) -> bool: ...
|
|
46
|
+
|
|
47
|
+
|
|
44
48
|
@dataclass(slots=True, frozen=True)
|
|
45
49
|
class YieldBase(ABC):
|
|
46
50
|
value: Any
|
|
@@ -643,6 +647,39 @@ def _get_async(back: int = 2) -> bool:
|
|
|
643
647
|
return False
|
|
644
648
|
|
|
645
649
|
|
|
650
|
+
@overload
|
|
651
|
+
def call_as_async[**Args, T: Coroutine](
|
|
652
|
+
func: Callable[Args, T], /,
|
|
653
|
+
threaded: bool = False,
|
|
654
|
+
) -> Callable[Args, T]:
|
|
655
|
+
...
|
|
656
|
+
@overload
|
|
657
|
+
def call_as_async[**Args, T](
|
|
658
|
+
func: Callable[Args, T], /,
|
|
659
|
+
threaded: bool = False,
|
|
660
|
+
) -> Callable[Args, Coroutine[Any, Any, T]]:
|
|
661
|
+
...
|
|
662
|
+
def call_as_async[**Args, T](
|
|
663
|
+
func: Callable[Args, T],
|
|
664
|
+
/,
|
|
665
|
+
threaded: bool = False,
|
|
666
|
+
) -> Callable[Args, T] | Callable[Args, Coroutine[Any, Any, T]]:
|
|
667
|
+
if iscoroutinefunction(func):
|
|
668
|
+
return func
|
|
669
|
+
def wraps(*args, **kwds):
|
|
670
|
+
try:
|
|
671
|
+
return func(*args, **kwds)
|
|
672
|
+
except (StopIteration, StopAsyncIteration) as e:
|
|
673
|
+
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)
|
|
680
|
+
return wrapper
|
|
681
|
+
|
|
682
|
+
|
|
646
683
|
def iter_gen_step(
|
|
647
684
|
gen_step: Generator | Callable[[], Generator],
|
|
648
685
|
):
|
|
@@ -650,7 +687,7 @@ def iter_gen_step(
|
|
|
650
687
|
gen = gen_step()
|
|
651
688
|
else:
|
|
652
689
|
gen = gen_step
|
|
653
|
-
send
|
|
690
|
+
send = gen.send
|
|
654
691
|
throw = gen.throw
|
|
655
692
|
close = gen.close
|
|
656
693
|
try:
|
|
@@ -681,6 +718,8 @@ def iter_gen_step(
|
|
|
681
718
|
except BaseException as e:
|
|
682
719
|
raise Reraised(e) from e
|
|
683
720
|
yield value
|
|
721
|
+
except BaseException as e:
|
|
722
|
+
raise Reraised(e) from e
|
|
684
723
|
finally:
|
|
685
724
|
close()
|
|
686
725
|
|
|
@@ -693,9 +732,9 @@ async def iter_gen_step_async(
|
|
|
693
732
|
gen = gen_step()
|
|
694
733
|
else:
|
|
695
734
|
gen = gen_step
|
|
696
|
-
send =
|
|
697
|
-
throw =
|
|
698
|
-
close =
|
|
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)
|
|
699
738
|
try:
|
|
700
739
|
value = await send(None)
|
|
701
740
|
while True:
|
|
@@ -703,64 +742,103 @@ async def iter_gen_step_async(
|
|
|
703
742
|
raise StopIteration(value)
|
|
704
743
|
try:
|
|
705
744
|
if callable(value):
|
|
706
|
-
value = await
|
|
745
|
+
value = await call_as_async(value, threaded=threaded)()
|
|
746
|
+
if isawaitable(value):
|
|
747
|
+
value = await value
|
|
707
748
|
except BaseException as e:
|
|
749
|
+
if isinstance(e, Reraised):
|
|
750
|
+
e = e.exception
|
|
708
751
|
value = await throw(e)
|
|
709
752
|
else:
|
|
710
753
|
value = await send(value)
|
|
711
754
|
yield value
|
|
712
|
-
except
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
identity =
|
|
718
|
-
try_call_me =
|
|
719
|
-
value
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
value = await to_thread(value)
|
|
724
|
-
else:
|
|
725
|
-
value = value()
|
|
726
|
-
if not identity and isawaitable(value):
|
|
727
|
-
value = await value
|
|
728
|
-
except BaseException as e:
|
|
755
|
+
except BaseException as e:
|
|
756
|
+
if isinstance(e, Reraised):
|
|
757
|
+
e = e.exception
|
|
758
|
+
if isinstance(e, StopIteration):
|
|
759
|
+
value = e.value
|
|
760
|
+
identity = False
|
|
761
|
+
try_call_me = True
|
|
762
|
+
if isinstance(value, YieldBase):
|
|
763
|
+
identity = value.identity
|
|
764
|
+
try_call_me = value.try_call_me
|
|
765
|
+
value = value.value
|
|
729
766
|
try:
|
|
730
|
-
value
|
|
767
|
+
if callable(value) and try_call_me:
|
|
768
|
+
value = await call_as_async(value, threaded=threaded)()
|
|
769
|
+
if not identity and isawaitable(value):
|
|
770
|
+
value = await value
|
|
731
771
|
except BaseException as e:
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
772
|
+
try:
|
|
773
|
+
value = await throw(e)
|
|
774
|
+
except BaseException as e:
|
|
775
|
+
raise Reraised(e) from e
|
|
776
|
+
yield value
|
|
777
|
+
else:
|
|
778
|
+
raise
|
|
736
779
|
finally:
|
|
737
780
|
await close()
|
|
738
781
|
|
|
739
782
|
|
|
740
783
|
def run_gen_step[T](
|
|
741
784
|
gen_step: Generator | Callable[[], Generator],
|
|
785
|
+
threaded: bool = False,
|
|
786
|
+
running_flag: None | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] | SupportsBool = None,
|
|
742
787
|
*,
|
|
743
788
|
async_: None | bool = None,
|
|
744
|
-
threaded: bool = False,
|
|
745
789
|
):
|
|
746
790
|
if async_ is None:
|
|
747
791
|
async_ = _get_async()
|
|
748
792
|
if async_:
|
|
749
793
|
async def process():
|
|
794
|
+
gen = iter_gen_step_async(gen_step, threaded=threaded)
|
|
750
795
|
try:
|
|
751
|
-
|
|
752
|
-
|
|
796
|
+
if running_flag is None:
|
|
797
|
+
async for value in gen:
|
|
798
|
+
pass
|
|
799
|
+
elif callable(running_flag):
|
|
800
|
+
pred = running_flag()
|
|
801
|
+
if not ((await pred) if isawaitable(pred) else pred):
|
|
802
|
+
raise RuntimeError("stop before starting", gen) from StopIteration()
|
|
803
|
+
async for value in gen:
|
|
804
|
+
pred = running_flag()
|
|
805
|
+
if not ((await pred) if isawaitable(pred) else pred):
|
|
806
|
+
raise RuntimeError("stop midway", gen) from StopIteration(value)
|
|
807
|
+
else:
|
|
808
|
+
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)
|
|
753
813
|
return value
|
|
754
814
|
except Reraised as e:
|
|
755
815
|
raise e.exception
|
|
816
|
+
except KeyboardInterrupt as e:
|
|
817
|
+
e.args = gen,
|
|
818
|
+
e.add_note(f"stop iterating gen_step: {gen!r}")
|
|
819
|
+
raise
|
|
756
820
|
return process()
|
|
757
821
|
else:
|
|
822
|
+
gen = iter_gen_step(gen_step)
|
|
758
823
|
try:
|
|
759
|
-
|
|
760
|
-
|
|
824
|
+
if running_flag is None:
|
|
825
|
+
for value in gen:
|
|
826
|
+
pass
|
|
827
|
+
else:
|
|
828
|
+
if not callable(running_flag):
|
|
829
|
+
running_flag = running_flag.__bool__
|
|
830
|
+
if not running_flag():
|
|
831
|
+
raise RuntimeError("stop before starting", gen) from StopIteration()
|
|
832
|
+
for value in gen:
|
|
833
|
+
if not running_flag():
|
|
834
|
+
raise RuntimeError("stop midway", gen) from StopIteration(value)
|
|
761
835
|
return value
|
|
762
836
|
except Reraised as e:
|
|
763
837
|
raise e.exception
|
|
838
|
+
except KeyboardInterrupt as e:
|
|
839
|
+
e.args = gen,
|
|
840
|
+
e.add_note(f"stop iterating gen_step: {gen!r}")
|
|
841
|
+
raise
|
|
764
842
|
|
|
765
843
|
|
|
766
844
|
@overload
|
|
@@ -799,30 +877,30 @@ def run_gen_step_iter(
|
|
|
799
877
|
gen = gen_step()
|
|
800
878
|
else:
|
|
801
879
|
gen = gen_step
|
|
802
|
-
send = gen.send
|
|
803
|
-
throw = gen.throw
|
|
804
|
-
close = gen.close
|
|
880
|
+
send: Callable = gen.send
|
|
881
|
+
throw: Callable = gen.throw
|
|
882
|
+
close: Callable = gen.close
|
|
805
883
|
if async_:
|
|
884
|
+
send = call_as_async(send, threaded=threaded)
|
|
885
|
+
throw = call_as_async(throw, threaded=threaded)
|
|
886
|
+
close = call_as_async(close, threaded=threaded)
|
|
806
887
|
async def process():
|
|
807
888
|
async def extract(value):
|
|
808
889
|
identity = False
|
|
809
890
|
try_call_me = True
|
|
810
891
|
yield_type = -1
|
|
811
892
|
if isinstance(value, YieldBase):
|
|
812
|
-
identity
|
|
893
|
+
identity = value.identity
|
|
813
894
|
try_call_me = value.try_call_me
|
|
814
|
-
yield_type
|
|
815
|
-
value
|
|
895
|
+
yield_type = value.yield_type
|
|
896
|
+
value = value.value
|
|
816
897
|
if try_call_me and callable(value):
|
|
817
|
-
value = value()
|
|
898
|
+
value = await call_as_async(value, threaded=threaded)()
|
|
818
899
|
if not identity and isawaitable(value):
|
|
819
900
|
value = await value
|
|
820
901
|
return yield_type, value
|
|
821
902
|
try:
|
|
822
|
-
|
|
823
|
-
value = await to_thread(send, None)
|
|
824
|
-
else:
|
|
825
|
-
value = send(None)
|
|
903
|
+
value = await send(None)
|
|
826
904
|
while True:
|
|
827
905
|
try:
|
|
828
906
|
yield_type, value = await extract(value)
|
|
@@ -833,34 +911,31 @@ def run_gen_step_iter(
|
|
|
833
911
|
async for val in ensure_aiter(value, threaded=threaded):
|
|
834
912
|
yield val
|
|
835
913
|
except BaseException as e:
|
|
836
|
-
if
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
value = throw(e)
|
|
914
|
+
if isinstance(e, Reraised):
|
|
915
|
+
e = e.exception
|
|
916
|
+
value = await throw(e)
|
|
840
917
|
else:
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
throw(e)
|
|
859
|
-
finally:
|
|
860
|
-
if threaded:
|
|
861
|
-
await to_thread(close)
|
|
918
|
+
value = await send(value)
|
|
919
|
+
except BaseException as e:
|
|
920
|
+
if isinstance(e, Reraised):
|
|
921
|
+
e = e.exception
|
|
922
|
+
if isinstance(e, StopIteration):
|
|
923
|
+
try:
|
|
924
|
+
yield_type, value = await extract(e.value)
|
|
925
|
+
match yield_type:
|
|
926
|
+
case 1:
|
|
927
|
+
yield value
|
|
928
|
+
case 2:
|
|
929
|
+
async for val in ensure_aiter(value, threaded=threaded):
|
|
930
|
+
yield val
|
|
931
|
+
except BaseException as e:
|
|
932
|
+
if isinstance(e, Reraised):
|
|
933
|
+
e = e.exception
|
|
934
|
+
await throw(e)
|
|
862
935
|
else:
|
|
863
|
-
|
|
936
|
+
raise
|
|
937
|
+
finally:
|
|
938
|
+
await close()
|
|
864
939
|
else:
|
|
865
940
|
def process():
|
|
866
941
|
def extract(value, /):
|
|
@@ -910,16 +985,28 @@ def run_gen_step_iter(
|
|
|
910
985
|
def as_gen_step(
|
|
911
986
|
func: Callable,
|
|
912
987
|
/,
|
|
913
|
-
async_: None | bool = None,
|
|
914
988
|
iter: bool = False,
|
|
989
|
+
threaded: bool = False,
|
|
990
|
+
running_flag: None | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] | SupportsBool = None,
|
|
991
|
+
*,
|
|
992
|
+
async_: None | bool = None,
|
|
915
993
|
) -> Callable:
|
|
916
994
|
if async_ is None:
|
|
917
995
|
async_ = _get_async()
|
|
918
996
|
def wrapper(*args, **kwds):
|
|
919
997
|
if iter:
|
|
920
|
-
return run_gen_step_iter(
|
|
998
|
+
return run_gen_step_iter(
|
|
999
|
+
func(*args, **kwds),
|
|
1000
|
+
threaded=threaded,
|
|
1001
|
+
async_=async_, # type: ignore
|
|
1002
|
+
)
|
|
921
1003
|
else:
|
|
922
|
-
return run_gen_step(
|
|
1004
|
+
return run_gen_step(
|
|
1005
|
+
func(*args, **kwds),
|
|
1006
|
+
threaded=threaded,
|
|
1007
|
+
running_flag=running_flag,
|
|
1008
|
+
async_=async_,
|
|
1009
|
+
)
|
|
923
1010
|
return wrapper
|
|
924
1011
|
|
|
925
1012
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|