python-iterutils 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

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.
iterutils/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 2, 1)
5
+ __version__ = (0, 2, 3)
6
6
  __all__ = [
7
7
  "Return", "Yield", "YieldFrom", "iterable", "async_iterable",
8
8
  "foreach", "async_foreach", "through", "async_through", "flatten",
@@ -10,7 +10,8 @@ __all__ = [
10
10
  "async_group_collect", "map", "filter", "reduce", "zip", "chunked",
11
11
  "iter_unique", "async_iter_unique", "wrap_iter", "wrap_aiter",
12
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",
13
+ "run_gen_step", "run_gen_step_sync_iter", "run_gen_step_async_iter",
14
+ "run_gen_step_iter", "as_gen_step", "as_gen_step_iter", "bfs_gen",
14
15
  "with_iter_next", "backgroud_loop",
15
16
  ]
16
17
 
@@ -656,7 +657,7 @@ def _get_async(back: int = 2) -> bool:
656
657
  f_locals = f.f_locals
657
658
  if f_locals is f_globals:
658
659
  return f_locals.get("async_") or False
659
- while f_locals is not f_globals:
660
+ while f_locals is not None and f_locals is not f_globals:
660
661
  if "async_" in f_locals:
661
662
  return f_locals["async_"] or False
662
663
  f = f.f_back
@@ -812,6 +813,7 @@ async def iter_gen_step_async(
812
813
  await close()
813
814
 
814
815
 
816
+ @overload
815
817
  def run_gen_step(
816
818
  gen_step: Generator | Callable[[], Generator],
817
819
  may_await: bool | Literal[1] = True,
@@ -819,7 +821,39 @@ def run_gen_step(
819
821
  threaded: bool = False,
820
822
  running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
821
823
  *,
822
- async_: None | bool = None,
824
+ async_: None = None,
825
+ ):
826
+ ...
827
+ @overload
828
+ def run_gen_step(
829
+ gen_step: Generator | Callable[[], Generator],
830
+ may_await: bool | Literal[1] = True,
831
+ may_call: bool | Literal[1] = True,
832
+ threaded: bool = False,
833
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
834
+ *,
835
+ async_: Literal[False],
836
+ ):
837
+ ...
838
+ @overload
839
+ def run_gen_step(
840
+ gen_step: Generator | Callable[[], Generator],
841
+ may_await: bool | Literal[1] = True,
842
+ may_call: bool | Literal[1] = True,
843
+ threaded: bool = False,
844
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
845
+ *,
846
+ async_: Literal[True],
847
+ ) -> Coroutine:
848
+ ...
849
+ def run_gen_step(
850
+ gen_step: Generator | Callable[[], Generator],
851
+ may_await: bool | Literal[1] = True,
852
+ may_call: bool | Literal[1] = True,
853
+ threaded: bool = False,
854
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
855
+ *,
856
+ async_: None | Literal[False, True] = None,
823
857
  ):
824
858
  """
825
859
  """
@@ -881,12 +915,138 @@ def run_gen_step(
881
915
  raise
882
916
 
883
917
 
918
+ def run_gen_step_sync_iter(
919
+ gen_step: Generator | Callable[[], Generator],
920
+ may_call: bool | Literal[1] = True,
921
+ ) -> Iterator:
922
+ """
923
+ """
924
+ if callable(gen_step):
925
+ gen_step = gen_step()
926
+ send: Callable = gen_step.send
927
+ throw: Callable = gen_step.throw
928
+ close: Callable = gen_step.close
929
+ def extract(value, may_call=may_call, /):
930
+ yield_type = -1
931
+ if isinstance(value, YieldBase):
932
+ yield_type = value.yield_type
933
+ maybe_callable = value.may_call
934
+ if maybe_callable is not None:
935
+ may_call = maybe_callable
936
+ value = value.value
937
+ if may_call is 1 or may_call and callable(value):
938
+ value = value()
939
+ return yield_type, value
940
+ try:
941
+ value = send(None)
942
+ while True:
943
+ try:
944
+ yield_type, value = extract(value)
945
+ match yield_type:
946
+ case 0:
947
+ return value
948
+ case 1:
949
+ yield value
950
+ case 2:
951
+ yield from value
952
+ except BaseException as e:
953
+ value = throw(e)
954
+ else:
955
+ value = send(value)
956
+ except StopIteration as e:
957
+ try:
958
+ yield_type, value = extract(e.value)
959
+ match yield_type:
960
+ case 1:
961
+ yield value
962
+ case 2:
963
+ yield from value
964
+ case _:
965
+ return value
966
+ except BaseException as e:
967
+ throw(e)
968
+ finally:
969
+ close()
970
+
971
+
972
+ async def run_gen_step_async_iter(
973
+ gen_step: Generator | Callable[[], Generator],
974
+ may_await: bool | Literal[1] = True,
975
+ may_call: bool | Literal[1] = True,
976
+ threaded: bool = False,
977
+ ) -> AsyncIterator:
978
+ """
979
+ """
980
+ if callable(gen_step):
981
+ gen_step = gen_step()
982
+ send: Callable = call_as_async(gen_step.send, threaded=threaded)
983
+ throw: Callable = call_as_async(gen_step.throw, threaded=threaded)
984
+ close: Callable = call_as_async(gen_step.close, threaded=threaded)
985
+ async def extract(value, may_await=may_await, may_call=may_call, /):
986
+ yield_type = -1
987
+ if isinstance(value, YieldBase):
988
+ yield_type = value.yield_type
989
+ maybe_awaitable = value.may_await
990
+ if maybe_awaitable is not None:
991
+ may_await = maybe_awaitable
992
+ maybe_callable = value.may_call
993
+ if maybe_callable is not None:
994
+ may_call = maybe_callable
995
+ value = value.value
996
+ if may_call is 1 or may_call and callable(value):
997
+ value = await call_as_async(
998
+ value, may_await=may_await, threaded=threaded)()
999
+ elif may_await is 1 or may_await and isawaitable(value):
1000
+ value = await value
1001
+ return yield_type, value
1002
+ try:
1003
+ value = await send(None)
1004
+ while True:
1005
+ try:
1006
+ yield_type, value = await extract(value)
1007
+ match yield_type:
1008
+ case 0:
1009
+ return
1010
+ case 1:
1011
+ yield value
1012
+ case 2:
1013
+ async for val in ensure_aiter(value, threaded=threaded):
1014
+ yield val
1015
+ except BaseException as e:
1016
+ if isinstance(e, Reraised):
1017
+ e = e.exception
1018
+ value = await throw(e)
1019
+ else:
1020
+ value = await send(value)
1021
+ except BaseException as e:
1022
+ if isinstance(e, Reraised):
1023
+ e = e.exception
1024
+ if isinstance(e, StopIteration):
1025
+ try:
1026
+ yield_type, value = await extract(e.value)
1027
+ match yield_type:
1028
+ case 1:
1029
+ yield value
1030
+ case 2:
1031
+ async for val in ensure_aiter(value, threaded=threaded):
1032
+ yield val
1033
+ except BaseException as e:
1034
+ if isinstance(e, Reraised):
1035
+ e = e.exception
1036
+ await throw(e)
1037
+ else:
1038
+ raise
1039
+ finally:
1040
+ await close()
1041
+
1042
+
884
1043
  @overload
885
1044
  def run_gen_step_iter(
886
1045
  gen_step: Generator | Callable[[], Generator],
887
1046
  may_await: bool | Literal[1] = True,
888
1047
  may_call: bool | Literal[1] = True,
889
1048
  threaded: bool = False,
1049
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
890
1050
  *,
891
1051
  async_: None = None,
892
1052
  ) -> Iterator | AsyncIterator:
@@ -897,6 +1057,7 @@ def run_gen_step_iter(
897
1057
  may_await: bool | Literal[1] = True,
898
1058
  may_call: bool | Literal[1] = True,
899
1059
  threaded: bool = False,
1060
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
900
1061
  *,
901
1062
  async_: Literal[False],
902
1063
  ) -> Iterator:
@@ -907,6 +1068,7 @@ def run_gen_step_iter(
907
1068
  may_await: bool | Literal[1] = True,
908
1069
  may_call: bool | Literal[1] = True,
909
1070
  threaded: bool = False,
1071
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
910
1072
  *,
911
1073
  async_: Literal[True],
912
1074
  ) -> AsyncIterator:
@@ -916,162 +1078,62 @@ def run_gen_step_iter(
916
1078
  may_await: bool | Literal[1] = True,
917
1079
  may_call: bool | Literal[1] = True,
918
1080
  threaded: bool = False,
1081
+ running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
919
1082
  *,
920
- async_: None | bool = None,
1083
+ async_: None | Literal[False, True] = None,
921
1084
  ) -> Iterator | AsyncIterator:
922
1085
  """
923
1086
  """
924
1087
  if async_ is None:
925
1088
  async_ = _get_async()
926
- gen = gen_step() if callable(gen_step) else gen_step
927
- send: Callable = gen.send
928
- throw: Callable = gen.throw
929
- close: Callable = gen.close
930
- default_may_await = may_await
931
- default_may_call = may_call
932
1089
  if async_:
933
- send = call_as_async(send, threaded=threaded)
934
- throw = call_as_async(throw, threaded=threaded)
935
- close = call_as_async(close, threaded=threaded)
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():
956
- try:
957
- value = await send(None)
958
- while True:
959
- try:
960
- yield_type, value = await aextract(value)
961
- match yield_type:
962
- case 0:
963
- return
964
- case 1:
965
- yield value
966
- case 2:
967
- async for val in ensure_aiter(value, threaded=threaded):
968
- yield val
969
- except BaseException as e:
970
- if isinstance(e, Reraised):
971
- e = e.exception
972
- value = await throw(e)
973
- else:
974
- value = await send(value)
975
- except BaseException as e:
976
- if isinstance(e, Reraised):
977
- e = e.exception
978
- if isinstance(e, StopIteration):
979
- try:
980
- yield_type, value = await aextract(e.value)
981
- match yield_type:
982
- case 1:
983
- yield value
984
- case 2:
985
- async for val in ensure_aiter(value, threaded=threaded):
986
- yield val
987
- except BaseException as e:
988
- if isinstance(e, Reraised):
989
- e = e.exception
990
- await throw(e)
991
- else:
992
- raise
993
- finally:
994
- await close()
995
- return aprocess()
1090
+ return run_gen_step_async_iter(
1091
+ gen_step,
1092
+ may_await=may_await,
1093
+ may_call=may_call,
1094
+ threaded=threaded,
1095
+ )
996
1096
  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
1009
- def process():
1010
- try:
1011
- value = send(None)
1012
- while True:
1013
- try:
1014
- yield_type, value = extract(value)
1015
- match yield_type:
1016
- case 0:
1017
- return value
1018
- case 1:
1019
- yield value
1020
- case 2:
1021
- yield from value
1022
- except BaseException as e:
1023
- value = throw(e)
1024
- else:
1025
- value = send(value)
1026
- except StopIteration as e:
1027
- try:
1028
- yield_type, value = extract(e.value)
1029
- match yield_type:
1030
- case 1:
1031
- yield value
1032
- case 2:
1033
- yield from value
1034
- case _:
1035
- return value
1036
- except BaseException as e:
1037
- throw(e)
1038
- finally:
1039
- close()
1040
- return process()
1097
+ return run_gen_step_sync_iter(gen_step, may_call=may_call)
1041
1098
 
1042
1099
 
1043
1100
  @optional
1044
1101
  def as_gen_step(
1045
- func: Callable,
1102
+ func: Callable[..., Generator],
1046
1103
  /,
1104
+ *deco_args,
1047
1105
  iter: bool = False,
1048
- may_await: bool | Literal[1] = True,
1049
- may_call: bool | Literal[1] = True,
1050
- threaded: bool = False,
1051
- running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
1052
- *,
1053
- async_: None | bool = None,
1106
+ **deco_kwds,
1054
1107
  ) -> Callable:
1055
- """装饰器,构建一个 gen_step 函数
1056
- """
1057
- def wrapper(*args, **kwds):
1058
- if iter:
1059
- return run_gen_step_iter(
1060
- func(*args, **kwds),
1061
- may_await=may_await,
1062
- may_call=may_call,
1063
- threaded=threaded,
1064
- async_=async_, # type: ignore
1065
- )
1066
- else:
1067
- return run_gen_step(
1068
- func(*args, **kwds),
1069
- may_await=may_await,
1070
- may_call=may_call,
1071
- threaded=threaded,
1072
- running_flag=running_flag,
1073
- async_=async_,
1074
- )
1108
+ """装饰器,把生成器封装成 gen_step 函数
1109
+ """
1110
+ call = run_gen_step_iter if iter else run_gen_step
1111
+ def wrapper(*args, async_: Literal[False, True] = False, **kwds):
1112
+ return call(
1113
+ func(*args, async_=async_, **kwds),
1114
+ *deco_args,
1115
+ async_=async_,
1116
+ **deco_kwds,
1117
+ )
1118
+ return wrapper
1119
+
1120
+
1121
+ @optional
1122
+ def as_gen_step_iter(
1123
+ func: Callable[..., Generator],
1124
+ /,
1125
+ *deco_args,
1126
+ **deco_kwds,
1127
+ ) -> Callable:
1128
+ """装饰器,把生成器封装成 gen_step 函数
1129
+ """
1130
+ def wrapper(*args, async_: Literal[False, True] = False, **kwds):
1131
+ return run_gen_step_iter(
1132
+ func(*args, async_=async_, **kwds),
1133
+ *deco_args,
1134
+ async_=async_,
1135
+ **deco_kwds,
1136
+ )
1075
1137
  return wrapper
1076
1138
 
1077
1139
 
@@ -1121,31 +1183,36 @@ def bfs_gen[T](
1121
1183
  def with_iter_next[T](
1122
1184
  iterable: Iterable[T],
1123
1185
  /,
1124
- async_: Literal[False] = False,
1186
+ async_: Literal[False],
1125
1187
  ) -> ContextManager[Callable[[], T]]:
1126
1188
  ...
1127
1189
  @overload
1128
1190
  def with_iter_next[T](
1129
- iterable: AsyncIterable[T],
1191
+ iterable: Iterable[T] | AsyncIterable[T],
1130
1192
  /,
1131
- async_: Literal[False] = False,
1193
+ async_: Literal[True],
1132
1194
  ) -> ContextManager[Callable[[], Awaitable[T]]]:
1133
1195
  ...
1134
1196
  @overload
1135
1197
  def with_iter_next[T](
1136
1198
  iterable: Iterable[T] | AsyncIterable[T],
1137
1199
  /,
1138
- async_: Literal[True],
1139
- ) -> ContextManager[Callable[[], Awaitable[T]]]:
1200
+ async_: None = None,
1201
+ ) -> ContextManager[Callable[[], T]] | ContextManager[Callable[[], Awaitable[T]]]:
1140
1202
  ...
1141
1203
  @contextmanager
1142
1204
  def with_iter_next[T](
1143
1205
  iterable: Iterable[T] | AsyncIterable[T],
1144
1206
  /,
1145
- async_: Literal[False, True] = False,
1207
+ async_: None | Literal[False, True] = None,
1146
1208
  ):
1147
1209
  """
1148
1210
  """
1211
+ if async_ is None:
1212
+ if not isinstance(iterable, Iterable):
1213
+ async_ = True
1214
+ else:
1215
+ async_ = _get_async()
1149
1216
  if async_:
1150
1217
  get_next: Callable[[], T] | Callable[[], Awaitable[T]] = ensure_aiter(iterable).__anext__
1151
1218
  elif isinstance(iterable, Iterable):
@@ -1169,7 +1236,7 @@ def with_iter_next[T](
1169
1236
  def context[T](
1170
1237
  func: Callable[..., T],
1171
1238
  *ctxs: ContextManager,
1172
- async_: Literal[False] = False,
1239
+ async_: Literal[False],
1173
1240
  ) -> T:
1174
1241
  ...
1175
1242
  @overload
@@ -1179,13 +1246,25 @@ def context[T](
1179
1246
  async_: Literal[True],
1180
1247
  ) -> Coroutine[Any, Any, T]:
1181
1248
  ...
1249
+ @overload
1182
1250
  def context[T](
1183
1251
  func: Callable[..., T] | Callable[..., Awaitable[T]],
1184
1252
  *ctxs: ContextManager | AsyncContextManager,
1185
- async_: Literal[False, True] = False,
1253
+ async_: None = None,
1254
+ ) -> T | Coroutine[Any, Any, T]:
1255
+ ...
1256
+ def context[T](
1257
+ func: Callable[..., T] | Callable[..., Awaitable[T]],
1258
+ *ctxs: ContextManager | AsyncContextManager,
1259
+ async_: None | Literal[False, True] = None,
1186
1260
  ) -> T | Coroutine[Any, Any, T]:
1187
1261
  """
1188
1262
  """
1263
+ if async_ is None:
1264
+ if iscoroutinefunction(func):
1265
+ async_ = True
1266
+ else:
1267
+ async_ = _get_async()
1189
1268
  if async_:
1190
1269
  async def call():
1191
1270
  args: list = []
@@ -1215,7 +1294,7 @@ def backgroud_loop(
1215
1294
  /,
1216
1295
  interval: int | float = 0.05,
1217
1296
  *,
1218
- async_: Literal[False] = False,
1297
+ async_: Literal[False],
1219
1298
  ) -> ContextManager:
1220
1299
  ...
1221
1300
  @overload
@@ -1227,15 +1306,29 @@ def backgroud_loop(
1227
1306
  async_: Literal[True],
1228
1307
  ) -> AsyncContextManager:
1229
1308
  ...
1309
+ @overload
1230
1310
  def backgroud_loop(
1231
1311
  call: None | Callable = None,
1232
1312
  /,
1233
1313
  interval: int | float = 0.05,
1234
1314
  *,
1235
- async_: Literal[False, True] = False,
1315
+ async_: None = None,
1316
+ ) -> ContextManager | AsyncContextManager:
1317
+ ...
1318
+ def backgroud_loop(
1319
+ call: None | Callable = None,
1320
+ /,
1321
+ interval: int | float = 0.05,
1322
+ *,
1323
+ async_: None | Literal[False, True] = None,
1236
1324
  ) -> ContextManager | AsyncContextManager:
1237
1325
  """
1238
1326
  """
1327
+ if async_ is None:
1328
+ if iscoroutinefunction(call):
1329
+ async_ = True
1330
+ else:
1331
+ async_ = _get_async()
1239
1332
  use_default_call = not callable(call)
1240
1333
  if use_default_call:
1241
1334
  start = time()
@@ -1254,6 +1347,7 @@ def backgroud_loop(
1254
1347
  sleep(interval)
1255
1348
  running = True
1256
1349
  if async_:
1350
+ @asynccontextmanager
1257
1351
  async def actx():
1258
1352
  nonlocal running
1259
1353
  try:
@@ -1264,8 +1358,9 @@ def backgroud_loop(
1264
1358
  task.cancel()
1265
1359
  if use_default_call:
1266
1360
  print("\r\x1b[K", end="")
1267
- return asynccontextmanager(actx)()
1361
+ return actx()
1268
1362
  else:
1363
+ @contextmanager
1269
1364
  def ctx():
1270
1365
  nonlocal running
1271
1366
  try:
@@ -1274,5 +1369,5 @@ def backgroud_loop(
1274
1369
  running = False
1275
1370
  if use_default_call:
1276
1371
  print("\r\x1b[K", end="")
1277
- return contextmanager(ctx)()
1372
+ return ctx()
1278
1373
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -0,0 +1,7 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ iterutils/__init__.py,sha256=egZxWU804STaONEuyAjvI2R-nUbTXx1wU4AECgYu0jg,39456
3
+ iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ python_iterutils-0.2.3.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
+ python_iterutils-0.2.3.dist-info/METADATA,sha256=ZdhvscMtLIfrgiGyeXC30ZTSXg2v0J98cLu9YpVt7uo,1467
6
+ python_iterutils-0.2.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
+ python_iterutils-0.2.3.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
- iterutils/__init__.py,sha256=_veJbrFYJld_cVSYbBnR37dSbGi2aSDAkNmLUEvuiXE,37134
3
- iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- python_iterutils-0.2.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
- python_iterutils-0.2.1.dist-info/METADATA,sha256=C55LpIsT807qqeehnsAZ7ZS-za5Gb03eltgrdYiN5vM,1467
6
- python_iterutils-0.2.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
- python_iterutils-0.2.1.dist-info/RECORD,,