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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.1.9
3
+ Version: 0.1.11
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,7 +2,7 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 1, 9)
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 = gen.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 = ensure_async(gen.send, threaded=threaded)
697
- throw = ensure_async(gen.throw, threaded=threaded)
698
- close = ensure_async(gen.close, threaded=threaded)
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 ensure_async(value, threaded=threaded)()
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 StopIteration as e:
713
- value = e.value
714
- identity = False
715
- try_call_me = True
716
- if isinstance(value, YieldBase):
717
- identity = value.identity
718
- try_call_me = value.try_call_me
719
- value = value.value
720
- try:
721
- if callable(value) and try_call_me:
722
- if threaded:
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 = await throw(e)
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
- raise Reraised(e) from e
733
- yield value
734
- except StopAsyncIteration as e:
735
- raise Reraised(e) from e
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
- async for value in iter_gen_step_async(gen_step, threaded=threaded):
752
- pass
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
- for value in iter_gen_step(gen_step):
760
- pass
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 = value.identity
893
+ identity = value.identity
813
894
  try_call_me = value.try_call_me
814
- yield_type = value.yield_type
815
- value = value.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
- if threaded:
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 threaded:
837
- value = await to_thread(throw, e)
838
- else:
839
- value = throw(e)
914
+ if isinstance(e, Reraised):
915
+ e = e.exception
916
+ value = await throw(e)
840
917
  else:
841
- if threaded:
842
- value = await to_thread(send, value)
843
- else:
844
- value = send(value)
845
- except StopIteration as e:
846
- try:
847
- yield_type, value = await extract(e.value)
848
- match yield_type:
849
- case 1:
850
- yield value
851
- case 2:
852
- async for val in ensure_aiter(value, threaded=threaded):
853
- yield val
854
- except BaseException as e:
855
- if threaded:
856
- await to_thread(throw, e)
857
- else:
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
- close()
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(func(*args, **kwds), async_=async_) # type: ignore
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(func(*args, **kwds), async_=async_)
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
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-iterutils"
3
- version = "0.1.9"
3
+ version = "0.1.11"
4
4
  description = "Python another itertools."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"