python-iterutils 0.2.1__py3-none-any.whl → 0.2.2__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, 2)
6
6
  __all__ = [
7
7
  "Return", "Yield", "YieldFrom", "iterable", "async_iterable",
8
8
  "foreach", "async_foreach", "through", "async_through", "flatten",
@@ -10,8 +10,9 @@ __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",
14
- "with_iter_next", "backgroud_loop",
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",
15
16
  ]
16
17
 
17
18
  from abc import ABC, abstractmethod
@@ -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
@@ -881,6 +882,131 @@ def run_gen_step(
881
882
  raise
882
883
 
883
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
+
884
1010
  @overload
885
1011
  def run_gen_step_iter(
886
1012
  gen_step: Generator | Callable[[], Generator],
@@ -923,121 +1049,15 @@ def run_gen_step_iter(
923
1049
  """
924
1050
  if async_ is None:
925
1051
  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
1052
  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()
1053
+ return run_gen_step_async_iter(
1054
+ gen_step,
1055
+ may_await=may_await,
1056
+ may_call=may_call,
1057
+ threaded=threaded,
1058
+ )
996
1059
  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()
1060
+ return run_gen_step_sync_iter(gen_step, may_call=may_call)
1041
1061
 
1042
1062
 
1043
1063
  @optional
@@ -1054,6 +1074,8 @@ def as_gen_step(
1054
1074
  ) -> Callable:
1055
1075
  """装饰器,构建一个 gen_step 函数
1056
1076
  """
1077
+ if async_ is None:
1078
+ async_ = _get_async()
1057
1079
  def wrapper(*args, **kwds):
1058
1080
  if iter:
1059
1081
  return run_gen_step_iter(
@@ -1121,31 +1143,36 @@ def bfs_gen[T](
1121
1143
  def with_iter_next[T](
1122
1144
  iterable: Iterable[T],
1123
1145
  /,
1124
- async_: Literal[False] = False,
1146
+ async_: Literal[False],
1125
1147
  ) -> ContextManager[Callable[[], T]]:
1126
1148
  ...
1127
1149
  @overload
1128
1150
  def with_iter_next[T](
1129
- iterable: AsyncIterable[T],
1151
+ iterable: Iterable[T] | AsyncIterable[T],
1130
1152
  /,
1131
- async_: Literal[False] = False,
1153
+ async_: Literal[True],
1132
1154
  ) -> ContextManager[Callable[[], Awaitable[T]]]:
1133
1155
  ...
1134
1156
  @overload
1135
1157
  def with_iter_next[T](
1136
1158
  iterable: Iterable[T] | AsyncIterable[T],
1137
1159
  /,
1138
- async_: Literal[True],
1139
- ) -> ContextManager[Callable[[], Awaitable[T]]]:
1160
+ async_: None = None,
1161
+ ) -> ContextManager[Callable[[], T]] | ContextManager[Callable[[], Awaitable[T]]]:
1140
1162
  ...
1141
1163
  @contextmanager
1142
1164
  def with_iter_next[T](
1143
1165
  iterable: Iterable[T] | AsyncIterable[T],
1144
1166
  /,
1145
- async_: Literal[False, True] = False,
1167
+ async_: None | Literal[False, True] = None,
1146
1168
  ):
1147
1169
  """
1148
1170
  """
1171
+ if async_ is None:
1172
+ if not isinstance(iterable, Iterable):
1173
+ async_ = True
1174
+ else:
1175
+ async_ = _get_async()
1149
1176
  if async_:
1150
1177
  get_next: Callable[[], T] | Callable[[], Awaitable[T]] = ensure_aiter(iterable).__anext__
1151
1178
  elif isinstance(iterable, Iterable):
@@ -1169,7 +1196,7 @@ def with_iter_next[T](
1169
1196
  def context[T](
1170
1197
  func: Callable[..., T],
1171
1198
  *ctxs: ContextManager,
1172
- async_: Literal[False] = False,
1199
+ async_: Literal[False],
1173
1200
  ) -> T:
1174
1201
  ...
1175
1202
  @overload
@@ -1179,13 +1206,25 @@ def context[T](
1179
1206
  async_: Literal[True],
1180
1207
  ) -> Coroutine[Any, Any, T]:
1181
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
+ ...
1182
1216
  def context[T](
1183
1217
  func: Callable[..., T] | Callable[..., Awaitable[T]],
1184
1218
  *ctxs: ContextManager | AsyncContextManager,
1185
- async_: Literal[False, True] = False,
1219
+ async_: None | Literal[False, True] = None,
1186
1220
  ) -> T | Coroutine[Any, Any, T]:
1187
1221
  """
1188
1222
  """
1223
+ if async_ is None:
1224
+ if iscoroutinefunction(func):
1225
+ async_ = True
1226
+ else:
1227
+ async_ = _get_async()
1189
1228
  if async_:
1190
1229
  async def call():
1191
1230
  args: list = []
@@ -1215,7 +1254,7 @@ def backgroud_loop(
1215
1254
  /,
1216
1255
  interval: int | float = 0.05,
1217
1256
  *,
1218
- async_: Literal[False] = False,
1257
+ async_: Literal[False],
1219
1258
  ) -> ContextManager:
1220
1259
  ...
1221
1260
  @overload
@@ -1227,15 +1266,29 @@ def backgroud_loop(
1227
1266
  async_: Literal[True],
1228
1267
  ) -> AsyncContextManager:
1229
1268
  ...
1269
+ @overload
1230
1270
  def backgroud_loop(
1231
1271
  call: None | Callable = None,
1232
1272
  /,
1233
1273
  interval: int | float = 0.05,
1234
1274
  *,
1235
- async_: Literal[False, True] = False,
1275
+ async_: None = None,
1276
+ ) -> ContextManager | AsyncContextManager:
1277
+ ...
1278
+ def backgroud_loop(
1279
+ call: None | Callable = None,
1280
+ /,
1281
+ interval: int | float = 0.05,
1282
+ *,
1283
+ async_: None | Literal[False, True] = None,
1236
1284
  ) -> ContextManager | AsyncContextManager:
1237
1285
  """
1238
1286
  """
1287
+ if async_ is None:
1288
+ if iscoroutinefunction(call):
1289
+ async_ = True
1290
+ else:
1291
+ async_ = _get_async()
1239
1292
  use_default_call = not callable(call)
1240
1293
  if use_default_call:
1241
1294
  start = time()
@@ -1254,6 +1307,7 @@ def backgroud_loop(
1254
1307
  sleep(interval)
1255
1308
  running = True
1256
1309
  if async_:
1310
+ @asynccontextmanager
1257
1311
  async def actx():
1258
1312
  nonlocal running
1259
1313
  try:
@@ -1264,8 +1318,9 @@ def backgroud_loop(
1264
1318
  task.cancel()
1265
1319
  if use_default_call:
1266
1320
  print("\r\x1b[K", end="")
1267
- return asynccontextmanager(actx)()
1321
+ return actx()
1268
1322
  else:
1323
+ @contextmanager
1269
1324
  def ctx():
1270
1325
  nonlocal running
1271
1326
  try:
@@ -1274,5 +1329,5 @@ def backgroud_loop(
1274
1329
  running = False
1275
1330
  if use_default_call:
1276
1331
  print("\r\x1b[K", end="")
1277
- return contextmanager(ctx)()
1332
+ return ctx()
1278
1333
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.2.1
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
@@ -0,0 +1,7 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ iterutils/__init__.py,sha256=yCkEoGSY0n4E-_BvLRuoi50j4Kwuu-banqxIf-QloX4,37966
3
+ iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ python_iterutils-0.2.2.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
+ python_iterutils-0.2.2.dist-info/METADATA,sha256=xWgHLL5Q8knQfbWJkn9nijnp14koK8cvm8lw-JGGGrE,1467
6
+ python_iterutils-0.2.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
+ python_iterutils-0.2.2.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,,