python-iterutils 0.2.3.1__tar.gz → 0.2.4.1__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.3.1 → python_iterutils-0.2.4.1}/PKG-INFO +1 -2
- {python_iterutils-0.2.3.1 → python_iterutils-0.2.4.1}/iterutils/__init__.py +439 -661
- {python_iterutils-0.2.3.1 → python_iterutils-0.2.4.1}/pyproject.toml +1 -2
- {python_iterutils-0.2.3.1 → python_iterutils-0.2.4.1}/LICENSE +0 -0
- {python_iterutils-0.2.3.1 → python_iterutils-0.2.4.1}/iterutils/py.typed +0 -0
- {python_iterutils-0.2.3.1 → python_iterutils-0.2.4.1}/readme.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-iterutils
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4.1
|
|
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
|
|
@@ -21,7 +21,6 @@ Classifier: Topic :: Software Development
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Dist: python-asynctools (>=0.1.2)
|
|
24
|
-
Requires-Dist: python-decotools (>=0.0.2)
|
|
25
24
|
Requires-Dist: python-texttools (>=0.0.3)
|
|
26
25
|
Requires-Dist: python-undefined (>=0.0.3)
|
|
27
26
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-iterutils
|
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 2,
|
|
5
|
+
__version__ = (0, 2, 4)
|
|
6
6
|
__all__ = [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
7
|
+
"Yield", "YieldFrom", "iterable", "async_iterable",
|
|
8
|
+
"run_gen_step", "run_gen_step_iter", "as_gen_step",
|
|
9
|
+
"as_gen_step_iter", "split_cm, ""with_iter_next",
|
|
10
|
+
"map", "filter", "reduce", "zip", "chunked",
|
|
11
|
+
"foreach", "async_foreach", "through", "async_through",
|
|
12
|
+
"flatten", "async_flatten", "collect", "async_collect",
|
|
13
|
+
"group_collect", "async_group_collect", "iter_unique",
|
|
14
|
+
"async_iter_unique", "wrap_iter", "wrap_aiter", "acc_step",
|
|
15
|
+
"cut_iter", "bfs_gen", "context", "backgroud_loop",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
|
-
from
|
|
19
|
-
from asyncio import create_task, sleep as async_sleep, to_thread
|
|
18
|
+
from asyncio import create_task, sleep as async_sleep
|
|
20
19
|
from builtins import map as _map, filter as _filter, zip as _zip
|
|
21
20
|
from collections import defaultdict, deque
|
|
22
21
|
from collections.abc import (
|
|
@@ -27,95 +26,481 @@ from collections.abc import (
|
|
|
27
26
|
)
|
|
28
27
|
from contextlib import (
|
|
29
28
|
asynccontextmanager, contextmanager, ExitStack, AsyncExitStack,
|
|
29
|
+
AbstractContextManager, AbstractAsyncContextManager,
|
|
30
30
|
)
|
|
31
31
|
from copy import copy
|
|
32
32
|
from dataclasses import dataclass
|
|
33
33
|
from itertools import batched, pairwise
|
|
34
|
-
from inspect import isawaitable, iscoroutinefunction
|
|
35
|
-
from sys import _getframe
|
|
34
|
+
from inspect import isawaitable, iscoroutinefunction, signature
|
|
35
|
+
from sys import _getframe, exc_info
|
|
36
36
|
from _thread import start_new_thread
|
|
37
37
|
from time import sleep, time
|
|
38
38
|
from types import FrameType
|
|
39
39
|
from typing import (
|
|
40
40
|
cast, overload, Any, AsyncContextManager, ContextManager, Literal,
|
|
41
|
-
Protocol,
|
|
42
41
|
)
|
|
43
42
|
|
|
44
43
|
from asynctools import (
|
|
45
44
|
async_filter, async_map, async_reduce, async_zip, async_batched,
|
|
46
45
|
ensure_async, ensure_aiter, collect as async_collect,
|
|
47
46
|
)
|
|
48
|
-
from decotools import optional
|
|
49
47
|
from texttools import format_time
|
|
50
48
|
from undefined import undefined
|
|
51
49
|
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
@dataclass(slots=True, frozen=True, unsafe_hash=True)
|
|
52
|
+
class Yield:
|
|
53
|
+
"""专供 `run_gen_step_iter`,说明值需要 yield 给用户
|
|
54
54
|
"""
|
|
55
|
+
value: Any
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(slots=True, frozen=True, unsafe_hash=True)
|
|
59
|
+
class YieldFrom:
|
|
60
|
+
"""专供 `run_gen_step_iter`,说明值需要解包后逐个 yield 给用户
|
|
55
61
|
"""
|
|
56
|
-
|
|
62
|
+
value: Any
|
|
57
63
|
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
def iterable(obj, /) -> bool:
|
|
66
|
+
"""判断对象是不是 Iterable
|
|
60
67
|
"""
|
|
68
|
+
return isinstance(obj, Iterable)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def async_iterable(obj, /) -> bool:
|
|
72
|
+
"""判断对象是不是 AsyncIterable
|
|
61
73
|
"""
|
|
62
|
-
|
|
63
|
-
if isinstance(exc, Reraised):
|
|
64
|
-
exc = exc.exception
|
|
65
|
-
self.exception: BaseException = exc
|
|
74
|
+
return isinstance(obj, AsyncIterable)
|
|
66
75
|
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
def _get_async(back: int = 2, /) -> bool:
|
|
78
|
+
"""往上查找,从最近的调用栈的命名空间中获取 `async_` 的值
|
|
70
79
|
"""
|
|
80
|
+
f: None | FrameType
|
|
81
|
+
f = _getframe(back)
|
|
82
|
+
f_globals = f.f_globals
|
|
83
|
+
f_locals = f.f_locals
|
|
84
|
+
if f_locals is f_globals:
|
|
85
|
+
return f_locals.get("async_") or False
|
|
86
|
+
while f_locals is not None and f_locals is not f_globals:
|
|
87
|
+
if "async_" in f_locals:
|
|
88
|
+
if f_locals["async_"] is not None:
|
|
89
|
+
return f_locals["async_"]
|
|
90
|
+
f = f.f_back
|
|
91
|
+
if f is None:
|
|
92
|
+
break
|
|
93
|
+
f_locals = f.f_locals
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run_gen_step(gen: Generator, /):
|
|
98
|
+
send = gen.send
|
|
99
|
+
try:
|
|
100
|
+
value: Any = send(None)
|
|
101
|
+
while True:
|
|
102
|
+
value = send(value)
|
|
103
|
+
except StopIteration as e:
|
|
104
|
+
return e.value
|
|
105
|
+
finally:
|
|
106
|
+
gen.close()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
async def _run_gen_step_async(gen: Generator, /):
|
|
110
|
+
send = gen.send
|
|
111
|
+
throw = gen.throw
|
|
112
|
+
try:
|
|
113
|
+
ret: Awaitable = send(None)
|
|
114
|
+
while True:
|
|
115
|
+
try:
|
|
116
|
+
value: Any = await ret
|
|
117
|
+
except BaseException as e:
|
|
118
|
+
ret = throw(e)
|
|
119
|
+
else:
|
|
120
|
+
ret = send(value)
|
|
121
|
+
except StopIteration as e:
|
|
122
|
+
if isawaitable(e.value):
|
|
123
|
+
return await e.value
|
|
124
|
+
return e.value
|
|
125
|
+
finally:
|
|
126
|
+
gen.close()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def run_gen_step[**Args](
|
|
130
|
+
gen_step: Generator | Callable[Args, Generator],
|
|
131
|
+
async_: None | Literal[False, True] = None,
|
|
132
|
+
/,
|
|
133
|
+
*args: Args.args,
|
|
134
|
+
**kwds: Args.kwargs,
|
|
135
|
+
):
|
|
136
|
+
"""驱动生成器运行,并返回其结果
|
|
71
137
|
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
138
|
+
if async_ is None:
|
|
139
|
+
async_ = _get_async()
|
|
140
|
+
if not isinstance(gen_step, Generator):
|
|
141
|
+
params = signature(gen_step).parameters
|
|
142
|
+
if ((param := params.get("async_")) and
|
|
143
|
+
(param.kind in (param.POSITIONAL_OR_KEYWORD, param.KEYWORD_ONLY))
|
|
144
|
+
):
|
|
145
|
+
kwds.setdefault("async_", async_)
|
|
146
|
+
gen_step = gen_step(*args, **kwds)
|
|
147
|
+
if async_:
|
|
148
|
+
return _run_gen_step_async(gen_step)
|
|
149
|
+
else:
|
|
150
|
+
return _run_gen_step(gen_step)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _run_gen_step_iter(gen: Generator, /) -> Iterator:
|
|
154
|
+
send = gen.send
|
|
155
|
+
try:
|
|
156
|
+
value: Any = send(None)
|
|
157
|
+
while True:
|
|
158
|
+
if isinstance(value, Yield):
|
|
159
|
+
yield value.value
|
|
160
|
+
elif isinstance(value, YieldFrom):
|
|
161
|
+
yield from value.value
|
|
162
|
+
value = send(value)
|
|
163
|
+
except StopIteration as e:
|
|
164
|
+
value = e.value
|
|
165
|
+
if isinstance(value, Yield):
|
|
166
|
+
yield value.value
|
|
167
|
+
elif isinstance(value, YieldFrom):
|
|
168
|
+
yield from value.value
|
|
169
|
+
finally:
|
|
170
|
+
gen.close()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def _run_gen_step_async_iter(gen: Generator, /) -> AsyncIterator:
|
|
174
|
+
send = gen.send
|
|
175
|
+
throw = gen.throw
|
|
176
|
+
try:
|
|
177
|
+
ret: Awaitable | Yield | YieldFrom = send(None)
|
|
178
|
+
while True:
|
|
179
|
+
try:
|
|
180
|
+
if isinstance(ret, Awaitable):
|
|
181
|
+
value: Any = await ret
|
|
182
|
+
else:
|
|
183
|
+
value = ret.value
|
|
184
|
+
if isawaitable(value):
|
|
185
|
+
value = await value
|
|
186
|
+
if isinstance(ret, Yield):
|
|
187
|
+
yield value
|
|
188
|
+
elif isinstance(value, AsyncIterable):
|
|
189
|
+
async for e in value:
|
|
190
|
+
yield e
|
|
191
|
+
else:
|
|
192
|
+
for e in value:
|
|
193
|
+
yield e
|
|
194
|
+
except BaseException as e:
|
|
195
|
+
ret = throw(e)
|
|
196
|
+
else:
|
|
197
|
+
ret = send(value)
|
|
198
|
+
except StopIteration as e:
|
|
199
|
+
val = e.value
|
|
200
|
+
if isinstance(val, (Yield, YieldFrom)):
|
|
201
|
+
value = val.value
|
|
202
|
+
if isawaitable(value):
|
|
203
|
+
value = await value
|
|
204
|
+
if isinstance(ret, Yield):
|
|
205
|
+
yield value
|
|
206
|
+
elif isinstance(value, AsyncIterable):
|
|
207
|
+
async for e in value:
|
|
208
|
+
yield e
|
|
209
|
+
else:
|
|
210
|
+
for e in value:
|
|
211
|
+
yield e
|
|
212
|
+
elif isawaitable(val):
|
|
213
|
+
await val
|
|
214
|
+
finally:
|
|
215
|
+
gen.close()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@overload
|
|
219
|
+
def run_gen_step_iter[**Args](
|
|
220
|
+
gen_step: Generator | Callable[Args, Generator],
|
|
221
|
+
async_: None = None,
|
|
222
|
+
/,
|
|
223
|
+
*args: Args.args,
|
|
224
|
+
**kwds: Args.kwargs,
|
|
225
|
+
) -> Iterator | AsyncIterator:
|
|
226
|
+
...
|
|
227
|
+
@overload
|
|
228
|
+
def run_gen_step_iter[**Args](
|
|
229
|
+
gen_step: Generator | Callable[Args, Generator],
|
|
230
|
+
async_: Literal[False] = False,
|
|
231
|
+
/,
|
|
232
|
+
*args: Args.args,
|
|
233
|
+
**kwds: Args.kwargs,
|
|
234
|
+
) -> Iterator:
|
|
235
|
+
...
|
|
236
|
+
@overload
|
|
237
|
+
def run_gen_step_iter[**Args](
|
|
238
|
+
gen_step: Generator | Callable[Args, Generator],
|
|
239
|
+
async_: Literal[True],
|
|
240
|
+
/,
|
|
241
|
+
*args: Args.args,
|
|
242
|
+
**kwds: Args.kwargs,
|
|
243
|
+
) -> AsyncIterator:
|
|
244
|
+
...
|
|
245
|
+
def run_gen_step_iter[**Args](
|
|
246
|
+
gen_step: Generator | Callable[Args, Generator],
|
|
247
|
+
async_: None | Literal[False, True] = None,
|
|
248
|
+
/,
|
|
249
|
+
*args: Args.args,
|
|
250
|
+
**kwds: Args.kwargs,
|
|
251
|
+
) -> Iterator | AsyncIterator:
|
|
252
|
+
"""驱动生成器运行,并从中返回可迭代而出的值
|
|
253
|
+
"""
|
|
254
|
+
if async_ is None:
|
|
255
|
+
async_ = _get_async()
|
|
256
|
+
if not isinstance(gen_step, Generator):
|
|
257
|
+
params = signature(gen_step).parameters
|
|
258
|
+
if ((param := params.get("async_")) and
|
|
259
|
+
(param.kind in (param.POSITIONAL_OR_KEYWORD, param.KEYWORD_ONLY))
|
|
260
|
+
):
|
|
261
|
+
kwds.setdefault("async_", async_)
|
|
262
|
+
gen_step = gen_step(*args, **kwds)
|
|
263
|
+
if async_:
|
|
264
|
+
return _run_gen_step_async_iter(gen_step)
|
|
265
|
+
else:
|
|
266
|
+
return _run_gen_step_iter(gen_step)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def as_gen_step[**Args](
|
|
270
|
+
gen_step: Callable[Args, Generator],
|
|
271
|
+
/,
|
|
272
|
+
) -> Callable[Args, Any]:
|
|
273
|
+
def wrapper(*args: Args.args, **kwds: Args.kwargs):
|
|
274
|
+
return run_gen_step(
|
|
275
|
+
gen_step,
|
|
276
|
+
cast(None | Literal[False, True], kwds.pop("async_", None)),
|
|
277
|
+
*args,
|
|
278
|
+
**kwds,
|
|
279
|
+
)
|
|
280
|
+
return wrapper
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def as_gen_step_iter[**Args](
|
|
284
|
+
gen_step: Callable[Args, Generator],
|
|
285
|
+
/,
|
|
286
|
+
) -> Callable[Args, Iterable | AsyncIterable]:
|
|
287
|
+
def wrapper(*args: Args.args, **kwds: Args.kwargs):
|
|
288
|
+
return run_gen_step_iter(
|
|
289
|
+
gen_step,
|
|
290
|
+
cast(None | Literal[False, True], kwds.pop("async_", None)),
|
|
291
|
+
*args,
|
|
292
|
+
**kwds,
|
|
293
|
+
)
|
|
294
|
+
return wrapper
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@overload
|
|
298
|
+
def split_cm[T](
|
|
299
|
+
cm: AbstractContextManager[T],
|
|
300
|
+
/,
|
|
301
|
+
) -> tuple[Callable[[], T], Callable[[], Any]]:
|
|
302
|
+
...
|
|
303
|
+
@overload
|
|
304
|
+
def split_cm[T](
|
|
305
|
+
cm: AbstractAsyncContextManager[T],
|
|
306
|
+
/,
|
|
307
|
+
) -> tuple[Callable[[], Coroutine[Any, Any, T]], Callable[[], Coroutine]]:
|
|
308
|
+
...
|
|
309
|
+
def split_cm[T](
|
|
310
|
+
cm: AbstractContextManager[T] | AbstractAsyncContextManager[T],
|
|
311
|
+
/,
|
|
312
|
+
) -> (
|
|
313
|
+
tuple[Callable[[], T], Callable[[], Any]] |
|
|
314
|
+
tuple[Callable[[], Coroutine[Any, Any, T]], Callable[[], Coroutine]]
|
|
315
|
+
):
|
|
316
|
+
"""拆分上下文管理器,以供 `run_gen_step` 和 `run_gen_step_iter` 使用
|
|
317
|
+
|
|
318
|
+
.. code:: python
|
|
319
|
+
|
|
320
|
+
if async_:
|
|
321
|
+
async def process():
|
|
322
|
+
async with cm as obj:
|
|
323
|
+
do_what_you_want()
|
|
324
|
+
return process()
|
|
325
|
+
else:
|
|
326
|
+
with cm as obj:
|
|
327
|
+
do_what_you_want()
|
|
328
|
+
|
|
329
|
+
大概相当于
|
|
330
|
+
|
|
331
|
+
.. code:: python
|
|
332
|
+
|
|
333
|
+
def gen_step():
|
|
334
|
+
enter, exit = split_cm(cm)
|
|
335
|
+
obj = yield enter()
|
|
336
|
+
try:
|
|
337
|
+
do_what_you_want()
|
|
338
|
+
finally:
|
|
339
|
+
yield exit()
|
|
340
|
+
|
|
341
|
+
run_gen_step(gen_step, async_=async_)
|
|
342
|
+
"""
|
|
343
|
+
if isinstance(cm, AbstractAsyncContextManager):
|
|
344
|
+
enter: Callable = cm.__aenter__
|
|
345
|
+
exit: Callable = cm.__aexit__
|
|
346
|
+
else:
|
|
347
|
+
enter = cm.__enter__
|
|
348
|
+
exit = cm.__exit__
|
|
349
|
+
return enter, lambda: exit(*exc_info())
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@overload
|
|
353
|
+
def with_iter_next[T](
|
|
354
|
+
iterable: Iterable[T],
|
|
355
|
+
/,
|
|
356
|
+
) -> ContextManager[Callable[[], T]]:
|
|
357
|
+
...
|
|
358
|
+
@overload
|
|
359
|
+
def with_iter_next[T](
|
|
360
|
+
iterable: AsyncIterable[T],
|
|
361
|
+
/,
|
|
362
|
+
) -> ContextManager[Callable[[], Awaitable[T]]]:
|
|
363
|
+
...
|
|
364
|
+
@contextmanager
|
|
365
|
+
def with_iter_next[T](
|
|
366
|
+
iterable: Iterable[T] | AsyncIterable[T],
|
|
367
|
+
/,
|
|
368
|
+
):
|
|
369
|
+
"""包装迭代器,以供 `run_gen_step` 和 `run_gen_step_iter` 使用
|
|
370
|
+
|
|
371
|
+
.. code:: python
|
|
75
372
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
373
|
+
if async_:
|
|
374
|
+
async def process():
|
|
375
|
+
async for e in iterable:
|
|
376
|
+
do_what_you_want()
|
|
377
|
+
return process()
|
|
378
|
+
else:
|
|
379
|
+
for e in iterable:
|
|
380
|
+
do_what_you_want()
|
|
381
|
+
|
|
382
|
+
大概相当于
|
|
383
|
+
|
|
384
|
+
.. code:: python
|
|
385
|
+
|
|
386
|
+
def gen_step():
|
|
387
|
+
with with_iter_next(iterable) as do_next:
|
|
388
|
+
while True:
|
|
389
|
+
e = yield do_next()
|
|
390
|
+
do_what_you_want()
|
|
391
|
+
|
|
392
|
+
run_gen_step(gen_step, async_=async_)
|
|
393
|
+
"""
|
|
394
|
+
if isinstance(iterable, AsyncIterable):
|
|
395
|
+
try:
|
|
396
|
+
yield aiter(iterable).__anext__
|
|
397
|
+
except StopAsyncIteration:
|
|
398
|
+
pass
|
|
399
|
+
else:
|
|
400
|
+
try:
|
|
401
|
+
yield iter(iterable).__next__
|
|
402
|
+
except StopIteration:
|
|
403
|
+
pass
|
|
80
404
|
|
|
81
405
|
|
|
82
|
-
|
|
406
|
+
def map(
|
|
407
|
+
function: None | Callable,
|
|
408
|
+
iterable: Iterable | AsyncIterable,
|
|
409
|
+
/,
|
|
410
|
+
*iterables: Iterable | AsyncIterable,
|
|
411
|
+
):
|
|
83
412
|
"""
|
|
84
413
|
"""
|
|
85
|
-
|
|
86
|
-
|
|
414
|
+
if (
|
|
415
|
+
iscoroutinefunction(function) or
|
|
416
|
+
isinstance(iterable, AsyncIterable) or
|
|
417
|
+
any(isinstance(i, AsyncIterable) for i in iterables)
|
|
418
|
+
):
|
|
419
|
+
if function is None:
|
|
420
|
+
if iterables:
|
|
421
|
+
return async_zip(iterable, *iterables)
|
|
422
|
+
else:
|
|
423
|
+
return iterable
|
|
424
|
+
return async_map(function, iterable, *iterables)
|
|
425
|
+
if function is None:
|
|
426
|
+
if iterables:
|
|
427
|
+
return _zip(iterable, *iterables)
|
|
428
|
+
else:
|
|
429
|
+
return iterable
|
|
430
|
+
return _map(function, iterable, *iterables)
|
|
87
431
|
|
|
88
432
|
|
|
89
|
-
|
|
433
|
+
def filter(
|
|
434
|
+
function: None | Callable,
|
|
435
|
+
iterable: Iterable | AsyncIterable,
|
|
436
|
+
/,
|
|
437
|
+
):
|
|
90
438
|
"""
|
|
91
439
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
440
|
+
if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
|
|
441
|
+
return async_filter(function, iterable)
|
|
442
|
+
return _filter(function, iterable)
|
|
94
443
|
|
|
95
444
|
|
|
96
|
-
|
|
445
|
+
def reduce(
|
|
446
|
+
function: Callable,
|
|
447
|
+
iterable: Iterable | AsyncIterable,
|
|
448
|
+
initial: Any = undefined,
|
|
449
|
+
/,
|
|
450
|
+
):
|
|
97
451
|
"""
|
|
98
452
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
453
|
+
if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
|
|
454
|
+
return async_reduce(function, iterable, initial)
|
|
455
|
+
from functools import reduce
|
|
456
|
+
if initial is undefined:
|
|
457
|
+
return reduce(function, iterable)
|
|
458
|
+
return reduce(function, iterable, initial)
|
|
101
459
|
|
|
102
460
|
|
|
103
|
-
def
|
|
461
|
+
def zip(
|
|
462
|
+
iterable: Iterable | AsyncIterable,
|
|
463
|
+
/,
|
|
464
|
+
*iterables: Iterable | AsyncIterable,
|
|
465
|
+
):
|
|
104
466
|
"""
|
|
105
467
|
"""
|
|
106
|
-
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
return False
|
|
468
|
+
if isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
|
|
469
|
+
return async_zip(iterable, *iterables)
|
|
470
|
+
return _zip(iterable, *iterables)
|
|
110
471
|
|
|
111
472
|
|
|
112
|
-
|
|
473
|
+
@overload
|
|
474
|
+
def chunked[T](
|
|
475
|
+
iterable: Iterable[T],
|
|
476
|
+
n: int = 1,
|
|
477
|
+
/,
|
|
478
|
+
) -> Iterator[Sequence[T]]:
|
|
479
|
+
...
|
|
480
|
+
@overload
|
|
481
|
+
def chunked[T](
|
|
482
|
+
iterable: AsyncIterable[T],
|
|
483
|
+
n: int = 1,
|
|
484
|
+
/,
|
|
485
|
+
) -> AsyncIterator[Sequence[T]]:
|
|
486
|
+
...
|
|
487
|
+
def chunked[T](
|
|
488
|
+
iterable: Iterable[T] | AsyncIterable[T],
|
|
489
|
+
n: int = 1,
|
|
490
|
+
/,
|
|
491
|
+
) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
|
|
113
492
|
"""
|
|
114
493
|
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
494
|
+
if n < 0:
|
|
495
|
+
n = 1
|
|
496
|
+
if isinstance(iterable, Sequence):
|
|
497
|
+
if n == 1:
|
|
498
|
+
return ((e,) for e in iterable)
|
|
499
|
+
return (iterable[i:j] for i, j in pairwise(range(0, len(iterable)+n, n)))
|
|
500
|
+
elif isinstance(iterable, Iterable):
|
|
501
|
+
return batched(iterable, n)
|
|
502
|
+
else:
|
|
503
|
+
return async_batched(iterable, n)
|
|
119
504
|
|
|
120
505
|
|
|
121
506
|
def foreach(
|
|
@@ -410,109 +795,9 @@ async def async_group_collect[K, V, C: Container, M: MutableMapping](
|
|
|
410
795
|
return mapping
|
|
411
796
|
|
|
412
797
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
iterable: Iterable
|
|
416
|
-
/,
|
|
417
|
-
*iterables: Iterable | AsyncIterable,
|
|
418
|
-
):
|
|
419
|
-
"""
|
|
420
|
-
"""
|
|
421
|
-
if (
|
|
422
|
-
iscoroutinefunction(function) or
|
|
423
|
-
isinstance(iterable, AsyncIterable) or
|
|
424
|
-
any(isinstance(i, AsyncIterable) for i in iterables)
|
|
425
|
-
):
|
|
426
|
-
if function is None:
|
|
427
|
-
if iterables:
|
|
428
|
-
return async_zip(iterable, *iterables)
|
|
429
|
-
else:
|
|
430
|
-
return iterable
|
|
431
|
-
return async_map(function, iterable, *iterables)
|
|
432
|
-
if function is None:
|
|
433
|
-
if iterables:
|
|
434
|
-
return _zip(iterable, *iterables)
|
|
435
|
-
else:
|
|
436
|
-
return iterable
|
|
437
|
-
return _map(function, iterable, *iterables)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
def filter(
|
|
441
|
-
function: None | Callable,
|
|
442
|
-
iterable: Iterable | AsyncIterable,
|
|
443
|
-
/,
|
|
444
|
-
):
|
|
445
|
-
"""
|
|
446
|
-
"""
|
|
447
|
-
if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
|
|
448
|
-
return async_filter(function, iterable)
|
|
449
|
-
return _filter(function, iterable)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
def reduce(
|
|
453
|
-
function: Callable,
|
|
454
|
-
iterable: Iterable | AsyncIterable,
|
|
455
|
-
initial: Any = undefined,
|
|
456
|
-
/,
|
|
457
|
-
):
|
|
458
|
-
"""
|
|
459
|
-
"""
|
|
460
|
-
if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
|
|
461
|
-
return async_reduce(function, iterable, initial)
|
|
462
|
-
from functools import reduce
|
|
463
|
-
if initial is undefined:
|
|
464
|
-
return reduce(function, iterable)
|
|
465
|
-
return reduce(function, iterable, initial)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
def zip(
|
|
469
|
-
iterable: Iterable | AsyncIterable,
|
|
470
|
-
/,
|
|
471
|
-
*iterables: Iterable | AsyncIterable,
|
|
472
|
-
):
|
|
473
|
-
"""
|
|
474
|
-
"""
|
|
475
|
-
if isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
|
|
476
|
-
return async_zip(iterable, *iterables)
|
|
477
|
-
return _zip(iterable, *iterables)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
@overload
|
|
481
|
-
def chunked[T](
|
|
482
|
-
iterable: Iterable[T],
|
|
483
|
-
n: int = 1,
|
|
484
|
-
/,
|
|
485
|
-
) -> Iterator[Sequence[T]]:
|
|
486
|
-
...
|
|
487
|
-
@overload
|
|
488
|
-
def chunked[T](
|
|
489
|
-
iterable: AsyncIterable[T],
|
|
490
|
-
n: int = 1,
|
|
491
|
-
/,
|
|
492
|
-
) -> AsyncIterator[Sequence[T]]:
|
|
493
|
-
...
|
|
494
|
-
def chunked[T](
|
|
495
|
-
iterable: Iterable[T] | AsyncIterable[T],
|
|
496
|
-
n: int = 1,
|
|
497
|
-
/,
|
|
498
|
-
) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
|
|
499
|
-
"""
|
|
500
|
-
"""
|
|
501
|
-
if n < 0:
|
|
502
|
-
n = 1
|
|
503
|
-
if isinstance(iterable, Sequence):
|
|
504
|
-
if n == 1:
|
|
505
|
-
return ((e,) for e in iterable)
|
|
506
|
-
return (iterable[i:j] for i, j in pairwise(range(0, len(iterable)+n, n)))
|
|
507
|
-
elif isinstance(iterable, Iterable):
|
|
508
|
-
return batched(iterable, n)
|
|
509
|
-
else:
|
|
510
|
-
return async_batched(iterable, n)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
@overload
|
|
514
|
-
def iter_unique[T](
|
|
515
|
-
iterable: Iterable[T],
|
|
798
|
+
@overload
|
|
799
|
+
def iter_unique[T](
|
|
800
|
+
iterable: Iterable[T],
|
|
516
801
|
/,
|
|
517
802
|
seen: None | MutableSet = None,
|
|
518
803
|
) -> Iterator[T]:
|
|
@@ -650,460 +935,6 @@ def cut_iter(
|
|
|
650
935
|
yield stop, stop - start
|
|
651
936
|
|
|
652
937
|
|
|
653
|
-
def _get_async(back: int = 2) -> bool:
|
|
654
|
-
f: None | FrameType
|
|
655
|
-
f = _getframe(back)
|
|
656
|
-
f_globals = f.f_globals
|
|
657
|
-
f_locals = f.f_locals
|
|
658
|
-
if f_locals is f_globals:
|
|
659
|
-
return f_locals.get("async_") or False
|
|
660
|
-
while f_locals is not None and f_locals is not f_globals:
|
|
661
|
-
if "async_" in f_locals:
|
|
662
|
-
return f_locals["async_"] or False
|
|
663
|
-
f = f.f_back
|
|
664
|
-
if f is None:
|
|
665
|
-
break
|
|
666
|
-
f_locals = f.f_locals
|
|
667
|
-
return False
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
@overload
|
|
671
|
-
def call_as_async[**Args, T: Coroutine](
|
|
672
|
-
func: Callable[Args, T], /,
|
|
673
|
-
may_await: bool | Literal[1] = False,
|
|
674
|
-
threaded: bool = False,
|
|
675
|
-
) -> Callable[Args, T]:
|
|
676
|
-
...
|
|
677
|
-
@overload
|
|
678
|
-
def call_as_async[**Args, T](
|
|
679
|
-
func: Callable[Args, T], /,
|
|
680
|
-
may_await: bool | Literal[1] = False,
|
|
681
|
-
threaded: bool = False,
|
|
682
|
-
) -> Callable[Args, Coroutine[Any, Any, T]]:
|
|
683
|
-
...
|
|
684
|
-
def call_as_async[**Args, T](
|
|
685
|
-
func: Callable[Args, T],
|
|
686
|
-
/,
|
|
687
|
-
may_await: bool | Literal[1] = False,
|
|
688
|
-
threaded: bool = False,
|
|
689
|
-
) -> Callable[Args, T] | Callable[Args, Coroutine[Any, Any, T]]:
|
|
690
|
-
"""
|
|
691
|
-
"""
|
|
692
|
-
if iscoroutinefunction(func):
|
|
693
|
-
return func
|
|
694
|
-
def wraps(*args, **kwds):
|
|
695
|
-
try:
|
|
696
|
-
return func(*args, **kwds)
|
|
697
|
-
except (StopIteration, StopAsyncIteration) as e:
|
|
698
|
-
raise Reraised(e) from e
|
|
699
|
-
async def wrapper(*args, **kwds):
|
|
700
|
-
if threaded:
|
|
701
|
-
value = await to_thread(wraps, *args, **kwds)
|
|
702
|
-
else:
|
|
703
|
-
value = wraps(*args, **kwds)
|
|
704
|
-
if may_await is 1 or may_await and isawaitable(value):
|
|
705
|
-
value = await value
|
|
706
|
-
return value
|
|
707
|
-
return wrapper
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
def iter_gen_step(
|
|
711
|
-
gen_step: Generator | Callable[[], Generator],
|
|
712
|
-
may_call: bool | Literal[1] = True,
|
|
713
|
-
):
|
|
714
|
-
"""
|
|
715
|
-
"""
|
|
716
|
-
if callable(gen_step):
|
|
717
|
-
gen_step = gen_step()
|
|
718
|
-
send = gen_step.send
|
|
719
|
-
throw = gen_step.throw
|
|
720
|
-
close = gen_step.close
|
|
721
|
-
value: Any = None
|
|
722
|
-
try:
|
|
723
|
-
while True:
|
|
724
|
-
if isinstance(value, YieldBase):
|
|
725
|
-
raise StopIteration(value)
|
|
726
|
-
try:
|
|
727
|
-
if may_call is 1 or may_call and callable(value):
|
|
728
|
-
value = value()
|
|
729
|
-
except BaseException as e:
|
|
730
|
-
value = throw(e)
|
|
731
|
-
else:
|
|
732
|
-
value = send(value)
|
|
733
|
-
yield value
|
|
734
|
-
except StopIteration as e:
|
|
735
|
-
value = e.value
|
|
736
|
-
if isinstance(value, YieldBase):
|
|
737
|
-
maybe_callable = value.may_call
|
|
738
|
-
if maybe_callable is not None:
|
|
739
|
-
may_call = maybe_callable
|
|
740
|
-
value = value.value
|
|
741
|
-
if may_call is 1 or may_call and callable(value):
|
|
742
|
-
try:
|
|
743
|
-
value = value()
|
|
744
|
-
except BaseException as e:
|
|
745
|
-
try:
|
|
746
|
-
value = throw(e)
|
|
747
|
-
except BaseException as e:
|
|
748
|
-
raise Reraised(e) from e
|
|
749
|
-
yield value
|
|
750
|
-
finally:
|
|
751
|
-
close()
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
async def iter_gen_step_async(
|
|
755
|
-
gen_step: Generator | Callable[[], Generator],
|
|
756
|
-
may_await: bool | Literal[1] = True,
|
|
757
|
-
may_call: bool | Literal[1] = True,
|
|
758
|
-
threaded: bool = False,
|
|
759
|
-
):
|
|
760
|
-
"""
|
|
761
|
-
"""
|
|
762
|
-
if callable(gen_step):
|
|
763
|
-
gen_step = gen_step()
|
|
764
|
-
send: Callable = call_as_async(gen_step.send, threaded=threaded)
|
|
765
|
-
throw: Callable = call_as_async(gen_step.throw, threaded=threaded)
|
|
766
|
-
close: Callable = call_as_async(gen_step.close, threaded=threaded)
|
|
767
|
-
value: Any = None
|
|
768
|
-
try:
|
|
769
|
-
while True:
|
|
770
|
-
if isinstance(value, YieldBase):
|
|
771
|
-
raise StopIteration(value)
|
|
772
|
-
try:
|
|
773
|
-
if may_call is 1 or may_call and callable(value):
|
|
774
|
-
value = await call_as_async(
|
|
775
|
-
value, may_await=may_await, threaded=threaded)()
|
|
776
|
-
elif may_await is 1 or may_await and isawaitable(value):
|
|
777
|
-
value = await value
|
|
778
|
-
except BaseException as e:
|
|
779
|
-
if isinstance(e, Reraised):
|
|
780
|
-
e = e.exception
|
|
781
|
-
value = await throw(e)
|
|
782
|
-
else:
|
|
783
|
-
value = await send(value)
|
|
784
|
-
yield value
|
|
785
|
-
except BaseException as e:
|
|
786
|
-
if isinstance(e, Reraised):
|
|
787
|
-
e = e.exception
|
|
788
|
-
if isinstance(e, StopIteration):
|
|
789
|
-
value = e.value
|
|
790
|
-
if isinstance(value, YieldBase):
|
|
791
|
-
maybe_awaitable = value.may_await
|
|
792
|
-
if maybe_awaitable is not None:
|
|
793
|
-
may_await = maybe_awaitable
|
|
794
|
-
maybe_callable = value.may_call
|
|
795
|
-
if maybe_callable is not None:
|
|
796
|
-
may_call = maybe_callable
|
|
797
|
-
value = value.value
|
|
798
|
-
try:
|
|
799
|
-
if may_call is 1 or may_call and callable(value):
|
|
800
|
-
value = await call_as_async(
|
|
801
|
-
value, may_await=may_await, threaded=threaded)()
|
|
802
|
-
elif may_await is 1 or may_await and isawaitable(value):
|
|
803
|
-
value = await value
|
|
804
|
-
except BaseException as e:
|
|
805
|
-
try:
|
|
806
|
-
value = await throw(e)
|
|
807
|
-
except BaseException as e:
|
|
808
|
-
raise Reraised(e) from e
|
|
809
|
-
yield value
|
|
810
|
-
else:
|
|
811
|
-
raise
|
|
812
|
-
finally:
|
|
813
|
-
await close()
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
def run_gen_step(
|
|
817
|
-
gen_step: Generator | Callable[[], Generator],
|
|
818
|
-
may_await: bool | Literal[1] = True,
|
|
819
|
-
may_call: bool | Literal[1] = True,
|
|
820
|
-
threaded: bool = False,
|
|
821
|
-
running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
|
|
822
|
-
*,
|
|
823
|
-
async_: None | Literal[False, True] = None,
|
|
824
|
-
):
|
|
825
|
-
"""
|
|
826
|
-
"""
|
|
827
|
-
if async_ is None:
|
|
828
|
-
async_ = _get_async()
|
|
829
|
-
if async_:
|
|
830
|
-
async def process():
|
|
831
|
-
gen = iter_gen_step_async(
|
|
832
|
-
gen_step,
|
|
833
|
-
may_await=may_await,
|
|
834
|
-
may_call=may_call,
|
|
835
|
-
threaded=threaded,
|
|
836
|
-
)
|
|
837
|
-
try:
|
|
838
|
-
if running_flag is None:
|
|
839
|
-
async for value in gen:
|
|
840
|
-
pass
|
|
841
|
-
elif callable(running_flag):
|
|
842
|
-
pred = call_as_async(running_flag, threaded=threaded)
|
|
843
|
-
if not await pred():
|
|
844
|
-
raise RuntimeError("stop before starting", gen) from StopIteration()
|
|
845
|
-
async for value in gen:
|
|
846
|
-
if not await pred():
|
|
847
|
-
raise RuntimeError("stop midway", gen) from StopIteration(value)
|
|
848
|
-
else:
|
|
849
|
-
if not running_flag:
|
|
850
|
-
raise RuntimeError("stop before starting", gen) from StopIteration()
|
|
851
|
-
async for value in gen:
|
|
852
|
-
if not running_flag:
|
|
853
|
-
raise RuntimeError("stop midway", gen) from StopIteration(value)
|
|
854
|
-
return value
|
|
855
|
-
except Reraised as e:
|
|
856
|
-
raise e.exception
|
|
857
|
-
except KeyboardInterrupt as e:
|
|
858
|
-
e.args = gen,
|
|
859
|
-
e.add_note(f"stop iterating gen_step: {gen!r}")
|
|
860
|
-
raise
|
|
861
|
-
return process()
|
|
862
|
-
else:
|
|
863
|
-
gen = iter_gen_step(gen_step, may_call=may_call)
|
|
864
|
-
try:
|
|
865
|
-
if running_flag is None:
|
|
866
|
-
for value in gen:
|
|
867
|
-
pass
|
|
868
|
-
else:
|
|
869
|
-
if not callable(running_flag):
|
|
870
|
-
running_flag = running_flag.__bool__
|
|
871
|
-
if not running_flag():
|
|
872
|
-
raise RuntimeError("stop before starting", gen) from StopIteration()
|
|
873
|
-
for value in gen:
|
|
874
|
-
if not running_flag():
|
|
875
|
-
raise RuntimeError("stop midway", gen) from StopIteration(value)
|
|
876
|
-
return value
|
|
877
|
-
except Reraised as e:
|
|
878
|
-
raise e.exception
|
|
879
|
-
except KeyboardInterrupt as e:
|
|
880
|
-
e.args = gen,
|
|
881
|
-
e.add_note(f"stop iterating gen_step: {gen!r}")
|
|
882
|
-
raise
|
|
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
|
-
|
|
1010
|
-
@overload
|
|
1011
|
-
def run_gen_step_iter(
|
|
1012
|
-
gen_step: Generator | Callable[[], Generator],
|
|
1013
|
-
may_await: bool | Literal[1] = True,
|
|
1014
|
-
may_call: bool | Literal[1] = True,
|
|
1015
|
-
threaded: bool = False,
|
|
1016
|
-
running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
|
|
1017
|
-
*,
|
|
1018
|
-
async_: None = None,
|
|
1019
|
-
) -> Iterator | AsyncIterator:
|
|
1020
|
-
...
|
|
1021
|
-
@overload
|
|
1022
|
-
def run_gen_step_iter(
|
|
1023
|
-
gen_step: Generator | Callable[[], Generator],
|
|
1024
|
-
may_await: bool | Literal[1] = True,
|
|
1025
|
-
may_call: bool | Literal[1] = True,
|
|
1026
|
-
threaded: bool = False,
|
|
1027
|
-
running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
|
|
1028
|
-
*,
|
|
1029
|
-
async_: Literal[False],
|
|
1030
|
-
) -> Iterator:
|
|
1031
|
-
...
|
|
1032
|
-
@overload
|
|
1033
|
-
def run_gen_step_iter(
|
|
1034
|
-
gen_step: Generator | Callable[[], Generator],
|
|
1035
|
-
may_await: bool | Literal[1] = True,
|
|
1036
|
-
may_call: bool | Literal[1] = True,
|
|
1037
|
-
threaded: bool = False,
|
|
1038
|
-
running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
|
|
1039
|
-
*,
|
|
1040
|
-
async_: Literal[True],
|
|
1041
|
-
) -> AsyncIterator:
|
|
1042
|
-
...
|
|
1043
|
-
def run_gen_step_iter(
|
|
1044
|
-
gen_step: Generator | Callable[[], Generator],
|
|
1045
|
-
may_await: bool | Literal[1] = True,
|
|
1046
|
-
may_call: bool | Literal[1] = True,
|
|
1047
|
-
threaded: bool = False,
|
|
1048
|
-
running_flag: None | SupportsBool | Callable[[], SupportsBool] | Callable[[], Awaitable[SupportsBool]] = None,
|
|
1049
|
-
*,
|
|
1050
|
-
async_: None | Literal[False, True] = None,
|
|
1051
|
-
) -> Iterator | AsyncIterator:
|
|
1052
|
-
"""
|
|
1053
|
-
"""
|
|
1054
|
-
if async_ is None:
|
|
1055
|
-
async_ = _get_async()
|
|
1056
|
-
if async_:
|
|
1057
|
-
return run_gen_step_async_iter(
|
|
1058
|
-
gen_step,
|
|
1059
|
-
may_await=may_await,
|
|
1060
|
-
may_call=may_call,
|
|
1061
|
-
threaded=threaded,
|
|
1062
|
-
)
|
|
1063
|
-
else:
|
|
1064
|
-
return run_gen_step_sync_iter(gen_step, may_call=may_call)
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
@optional
|
|
1068
|
-
def as_gen_step(
|
|
1069
|
-
func: Callable[..., Generator],
|
|
1070
|
-
/,
|
|
1071
|
-
*deco_args,
|
|
1072
|
-
iter: bool = False,
|
|
1073
|
-
**deco_kwds,
|
|
1074
|
-
) -> Callable:
|
|
1075
|
-
"""装饰器,把生成器封装成 gen_step 函数
|
|
1076
|
-
"""
|
|
1077
|
-
call = run_gen_step_iter if iter else run_gen_step
|
|
1078
|
-
def wrapper(*args, async_: Literal[False, True] = False, **kwds):
|
|
1079
|
-
return call(
|
|
1080
|
-
func(*args, async_=async_, **kwds),
|
|
1081
|
-
*deco_args,
|
|
1082
|
-
async_=async_,
|
|
1083
|
-
**deco_kwds,
|
|
1084
|
-
)
|
|
1085
|
-
return wrapper
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
@optional
|
|
1089
|
-
def as_gen_step_iter(
|
|
1090
|
-
func: Callable[..., Generator],
|
|
1091
|
-
/,
|
|
1092
|
-
*deco_args,
|
|
1093
|
-
**deco_kwds,
|
|
1094
|
-
) -> Callable:
|
|
1095
|
-
"""装饰器,把生成器封装成 gen_step 函数
|
|
1096
|
-
"""
|
|
1097
|
-
def wrapper(*args, async_: Literal[False, True] = False, **kwds):
|
|
1098
|
-
return run_gen_step_iter(
|
|
1099
|
-
func(*args, async_=async_, **kwds),
|
|
1100
|
-
*deco_args,
|
|
1101
|
-
async_=async_,
|
|
1102
|
-
**deco_kwds,
|
|
1103
|
-
)
|
|
1104
|
-
return wrapper
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
938
|
@overload
|
|
1108
939
|
def bfs_gen[T](
|
|
1109
940
|
initial: T,
|
|
@@ -1146,59 +977,6 @@ def bfs_gen[T](
|
|
|
1146
977
|
args = yield val
|
|
1147
978
|
|
|
1148
979
|
|
|
1149
|
-
@overload
|
|
1150
|
-
def with_iter_next[T](
|
|
1151
|
-
iterable: Iterable[T],
|
|
1152
|
-
/,
|
|
1153
|
-
async_: Literal[False],
|
|
1154
|
-
) -> ContextManager[Callable[[], T]]:
|
|
1155
|
-
...
|
|
1156
|
-
@overload
|
|
1157
|
-
def with_iter_next[T](
|
|
1158
|
-
iterable: Iterable[T] | AsyncIterable[T],
|
|
1159
|
-
/,
|
|
1160
|
-
async_: Literal[True],
|
|
1161
|
-
) -> ContextManager[Callable[[], Awaitable[T]]]:
|
|
1162
|
-
...
|
|
1163
|
-
@overload
|
|
1164
|
-
def with_iter_next[T](
|
|
1165
|
-
iterable: Iterable[T] | AsyncIterable[T],
|
|
1166
|
-
/,
|
|
1167
|
-
async_: None = None,
|
|
1168
|
-
) -> ContextManager[Callable[[], T]] | ContextManager[Callable[[], Awaitable[T]]]:
|
|
1169
|
-
...
|
|
1170
|
-
@contextmanager
|
|
1171
|
-
def with_iter_next[T](
|
|
1172
|
-
iterable: Iterable[T] | AsyncIterable[T],
|
|
1173
|
-
/,
|
|
1174
|
-
async_: None | Literal[False, True] = None,
|
|
1175
|
-
):
|
|
1176
|
-
"""
|
|
1177
|
-
"""
|
|
1178
|
-
if async_ is None:
|
|
1179
|
-
if not isinstance(iterable, Iterable):
|
|
1180
|
-
async_ = True
|
|
1181
|
-
else:
|
|
1182
|
-
async_ = _get_async()
|
|
1183
|
-
if async_:
|
|
1184
|
-
get_next: Callable[[], T] | Callable[[], Awaitable[T]] = ensure_aiter(iterable).__anext__
|
|
1185
|
-
elif isinstance(iterable, Iterable):
|
|
1186
|
-
get_next = iter(iterable).__next__
|
|
1187
|
-
else:
|
|
1188
|
-
get_next = aiter(iterable).__anext__
|
|
1189
|
-
async_ = True
|
|
1190
|
-
if async_:
|
|
1191
|
-
try:
|
|
1192
|
-
yield get_next
|
|
1193
|
-
except StopAsyncIteration:
|
|
1194
|
-
pass
|
|
1195
|
-
else:
|
|
1196
|
-
try:
|
|
1197
|
-
yield get_next
|
|
1198
|
-
except StopIteration:
|
|
1199
|
-
pass
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
980
|
@overload
|
|
1203
981
|
def context[T](
|
|
1204
982
|
func: Callable[..., T],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-iterutils"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4.1"
|
|
4
4
|
description = "Python another itertools."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -28,7 +28,6 @@ include = [
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.12"
|
|
30
30
|
python-asynctools = ">=0.1.2"
|
|
31
|
-
python-decotools = ">=0.0.2"
|
|
32
31
|
python-texttools = ">=0.0.3"
|
|
33
32
|
python-undefined = ">=0.0.3"
|
|
34
33
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|