python-iterutils 0.2.1__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.
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/PKG-INFO +1 -1
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/iterutils/__init__.py +184 -129
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/pyproject.toml +1 -1
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/LICENSE +0 -0
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/iterutils/py.typed +0 -0
- {python_iterutils-0.2.1 → python_iterutils-0.2.2}/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, 2,
|
|
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", "
|
|
14
|
-
"
|
|
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
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
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]
|
|
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[
|
|
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_:
|
|
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] =
|
|
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]
|
|
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] =
|
|
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]
|
|
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_:
|
|
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
|
|
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
|
|
1332
|
+
return ctx()
|
|
1278
1333
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|