python-iterutils 0.0.9__tar.gz → 0.1.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.0.9 → python_iterutils-0.1.1}/PKG-INFO +3 -5
- {python_iterutils-0.0.9 → python_iterutils-0.1.1}/iterutils/__init__.py +227 -59
- {python_iterutils-0.0.9 → python_iterutils-0.1.1}/pyproject.toml +4 -4
- {python_iterutils-0.0.9 → python_iterutils-0.1.1}/LICENSE +0 -0
- {python_iterutils-0.0.9 → python_iterutils-0.1.1}/iterutils/py.typed +0 -0
- {python_iterutils-0.0.9 → python_iterutils-0.1.1}/readme.md +0 -0
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-iterutils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.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
|
|
7
7
|
Keywords: iterable,iterutils
|
|
8
8
|
Author: ChenyangGao
|
|
9
9
|
Author-email: wosiwujm@gmail.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
19
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
20
|
Classifier: Topic :: Software Development
|
|
23
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
-
Requires-Dist: python-asynctools (>=0.0.
|
|
23
|
+
Requires-Dist: python-asynctools (>=0.0.9)
|
|
26
24
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-iterutils
|
|
27
25
|
Description-Content-Type: text/markdown
|
|
28
26
|
|
|
@@ -2,26 +2,27 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0,
|
|
5
|
+
__version__ = (0, 1, 1)
|
|
6
6
|
__all__ = [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
7
|
+
"Return", "Yield", "YieldFrom", "iterable", "async_iterable", "foreach", "async_foreach",
|
|
8
|
+
"through", "async_through", "flatten", "async_flatten", "chunked", "wrap_iter", "wrap_aiter",
|
|
9
|
+
"acc_step", "cut_iter", "run_gen_step", "run_gen_step_iter", "bfs_gen",
|
|
10
10
|
]
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
from abc import ABC, abstractmethod
|
|
13
14
|
from asyncio import to_thread
|
|
15
|
+
from collections import deque
|
|
14
16
|
from collections.abc import (
|
|
15
|
-
AsyncIterable, AsyncIterator, Callable, Generator,
|
|
17
|
+
AsyncIterable, AsyncIterator, Buffer, Callable, Generator,
|
|
18
|
+
Iterable, Iterator, Sequence,
|
|
16
19
|
)
|
|
17
20
|
from dataclasses import dataclass
|
|
21
|
+
from itertools import batched, pairwise
|
|
18
22
|
from inspect import isawaitable
|
|
19
|
-
from typing import overload, Any, Literal
|
|
20
|
-
|
|
21
|
-
from asynctools import async_map, async_zip, ensure_async, ensure_aiter
|
|
22
|
-
|
|
23
|
+
from typing import overload, Any, Literal
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
from asynctools import async_map, async_zip, async_batched, ensure_async, ensure_aiter
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
@dataclass(slots=True, frozen=True)
|
|
@@ -48,21 +49,28 @@ class YieldFrom(YieldBase):
|
|
|
48
49
|
yield_type = 2
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def iterable(
|
|
52
|
+
def iterable(iterable, /) -> bool:
|
|
52
53
|
try:
|
|
53
|
-
return isinstance(iter(
|
|
54
|
+
return isinstance(iter(iterable), Iterable)
|
|
54
55
|
except TypeError:
|
|
55
56
|
return False
|
|
56
57
|
|
|
57
58
|
|
|
58
|
-
def async_iterable(
|
|
59
|
+
def async_iterable(iterable, /) -> bool:
|
|
59
60
|
try:
|
|
60
|
-
return isinstance(iter(
|
|
61
|
+
return isinstance(iter(iterable), AsyncIterable)
|
|
61
62
|
except TypeError:
|
|
62
63
|
return False
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
def foreach(
|
|
66
|
+
def foreach(
|
|
67
|
+
value: Callable,
|
|
68
|
+
iterable: Iterable | AsyncIterable,
|
|
69
|
+
/,
|
|
70
|
+
*iterables: Iterable | AsyncIterable,
|
|
71
|
+
):
|
|
72
|
+
if not (isinstance(iterable, Iterable) and all(isinstance(it, Iterable) for it in iterables)):
|
|
73
|
+
return async_foreach(value, iterable, *iterables)
|
|
66
74
|
if iterables:
|
|
67
75
|
for args in zip(iterable, *iterables):
|
|
68
76
|
value(*args)
|
|
@@ -71,7 +79,13 @@ def foreach(value: Callable, iterable, /, *iterables):
|
|
|
71
79
|
value(arg)
|
|
72
80
|
|
|
73
81
|
|
|
74
|
-
async def async_foreach(
|
|
82
|
+
async def async_foreach(
|
|
83
|
+
value: Callable,
|
|
84
|
+
iterable: Iterable | AsyncIterable,
|
|
85
|
+
/,
|
|
86
|
+
*iterables: Iterable | AsyncIterable,
|
|
87
|
+
threaded: bool = True,
|
|
88
|
+
):
|
|
75
89
|
value = ensure_async(value, threaded=threaded)
|
|
76
90
|
if iterables:
|
|
77
91
|
async for args in async_zip(iterable, *iterables, threaded=threaded):
|
|
@@ -81,76 +95,188 @@ async def async_foreach(value: Callable, iterable, /, *iterables, threaded: bool
|
|
|
81
95
|
await value(arg)
|
|
82
96
|
|
|
83
97
|
|
|
84
|
-
def through(
|
|
98
|
+
def through(
|
|
99
|
+
iterable: Iterable | AsyncIterable,
|
|
100
|
+
/,
|
|
101
|
+
take_while: None | Callable = None,
|
|
102
|
+
):
|
|
103
|
+
if not isinstance(iterable, Iterable):
|
|
104
|
+
return async_through(iterable, take_while)
|
|
85
105
|
if take_while is None:
|
|
86
|
-
for _ in
|
|
106
|
+
for _ in iterable:
|
|
87
107
|
pass
|
|
88
108
|
else:
|
|
89
|
-
for v in map(take_while,
|
|
109
|
+
for v in map(take_while, iterable):
|
|
90
110
|
if not v:
|
|
91
111
|
break
|
|
92
112
|
|
|
93
113
|
|
|
94
114
|
async def async_through(
|
|
95
|
-
|
|
115
|
+
iterable: Iterable | AsyncIterable,
|
|
96
116
|
/,
|
|
97
117
|
take_while: None | Callable = None,
|
|
98
118
|
threaded: bool = True,
|
|
99
119
|
):
|
|
100
|
-
|
|
120
|
+
iterable = ensure_aiter(iterable, threaded=threaded)
|
|
101
121
|
if take_while is None:
|
|
102
|
-
async for _ in
|
|
122
|
+
async for _ in iterable:
|
|
103
123
|
pass
|
|
104
124
|
elif take_while is bool:
|
|
105
|
-
async for v in
|
|
125
|
+
async for v in iterable:
|
|
106
126
|
if not v:
|
|
107
127
|
break
|
|
108
128
|
else:
|
|
109
|
-
async for v in async_map(take_while,
|
|
129
|
+
async for v in async_map(take_while, iterable):
|
|
110
130
|
if not v:
|
|
111
131
|
break
|
|
112
132
|
|
|
113
133
|
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
@overload
|
|
135
|
+
def flatten(
|
|
136
|
+
iterable: Iterable,
|
|
137
|
+
/,
|
|
138
|
+
exclude_types: type | tuple[type, ...] = (Buffer, str),
|
|
139
|
+
) -> Iterable:
|
|
140
|
+
...
|
|
141
|
+
@overload
|
|
142
|
+
def flatten(
|
|
143
|
+
iterable: AsyncIterable,
|
|
144
|
+
/,
|
|
145
|
+
exclude_types: type | tuple[type, ...] = (Buffer, str),
|
|
146
|
+
) -> AsyncIterable:
|
|
147
|
+
...
|
|
148
|
+
def flatten(
|
|
149
|
+
iterable: Iterable | AsyncIterable,
|
|
150
|
+
/,
|
|
151
|
+
exclude_types: type | tuple[type, ...] = (Buffer, str),
|
|
152
|
+
) -> Iterable | AsyncIterable:
|
|
153
|
+
if not isinstance(iterable, Iterable):
|
|
154
|
+
return async_flatten(iterable, exclude_types)
|
|
155
|
+
def gen(iterable):
|
|
156
|
+
for e in iterable:
|
|
157
|
+
if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
|
|
158
|
+
yield from gen(e)
|
|
159
|
+
else:
|
|
160
|
+
yield e
|
|
161
|
+
return gen(iterable)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
async def async_flatten(
|
|
165
|
+
iterable: Iterable | AsyncIterable,
|
|
166
|
+
/,
|
|
167
|
+
exclude_types: type | tuple[type, ...] = (Buffer, str),
|
|
168
|
+
threaded: bool = True,
|
|
169
|
+
) -> AsyncIterator:
|
|
170
|
+
async for e in ensure_aiter(iterable, threaded=threaded):
|
|
171
|
+
if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
|
|
172
|
+
async for e in async_flatten(e, exclude_types, threaded=threaded):
|
|
173
|
+
yield e
|
|
174
|
+
else:
|
|
175
|
+
yield e
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@overload
|
|
179
|
+
def chunked[T](
|
|
180
|
+
iterable: Iterable[T],
|
|
181
|
+
n: int = 1,
|
|
182
|
+
/,
|
|
183
|
+
) -> Iterator[Sequence[T]]:
|
|
184
|
+
...
|
|
185
|
+
@overload
|
|
186
|
+
def chunked[T](
|
|
187
|
+
iterable: AsyncIterable[T],
|
|
188
|
+
n: int = 1,
|
|
189
|
+
/,
|
|
190
|
+
) -> AsyncIterator[Sequence[T]]:
|
|
191
|
+
...
|
|
192
|
+
def chunked[T](
|
|
193
|
+
iterable: Iterable[T] | AsyncIterable[T],
|
|
194
|
+
n: int = 1,
|
|
195
|
+
/,
|
|
196
|
+
) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
|
|
197
|
+
if n < 0:
|
|
198
|
+
n = 1
|
|
199
|
+
if isinstance(iterable, Sequence):
|
|
200
|
+
if n == 1:
|
|
201
|
+
return ((e,) for e in iterable)
|
|
202
|
+
return (iterable[i:j] for i, j in pairwise(range(0, len(iterable)+n, n)))
|
|
203
|
+
elif isinstance(iterable, Iterable):
|
|
204
|
+
return batched(iterable, n)
|
|
205
|
+
else:
|
|
206
|
+
return async_batched(iterable, n)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@overload
|
|
210
|
+
def wrap_iter[T](
|
|
211
|
+
iterable: Iterable[T],
|
|
116
212
|
/,
|
|
117
213
|
callprev: None | Callable[[T], Any] = None,
|
|
118
214
|
callnext: None | Callable[[T], Any] = None,
|
|
119
215
|
callenter: None | Callable[[Iterable[T]], Any] = None,
|
|
120
216
|
callexit: None | Callable[[Iterable[T], None | BaseException], Any] = None,
|
|
121
217
|
) -> Iterator[T]:
|
|
218
|
+
...
|
|
219
|
+
@overload
|
|
220
|
+
def wrap_iter[T](
|
|
221
|
+
iterable: AsyncIterable[T],
|
|
222
|
+
/,
|
|
223
|
+
callprev: None | Callable[[T], Any] = None,
|
|
224
|
+
callnext: None | Callable[[T], Any] = None,
|
|
225
|
+
callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
|
|
226
|
+
callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
|
|
227
|
+
) -> AsyncIterator[T]:
|
|
228
|
+
...
|
|
229
|
+
def wrap_iter[T](
|
|
230
|
+
iterable: Iterable[T] | AsyncIterable[T],
|
|
231
|
+
/,
|
|
232
|
+
callprev: None | Callable[[T], Any] = None,
|
|
233
|
+
callnext: None | Callable[[T], Any] = None,
|
|
234
|
+
callenter: None | Callable[[Iterable[T]], Any] | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
|
|
235
|
+
callexit: ( None | Callable[[Iterable[T], None | BaseException], Any] |
|
|
236
|
+
Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] ) = None,
|
|
237
|
+
) -> Iterator[T] | AsyncIterator[T]:
|
|
238
|
+
if not isinstance(iterable, Iterable):
|
|
239
|
+
return wrap_aiter(
|
|
240
|
+
iterable,
|
|
241
|
+
callprev=callprev,
|
|
242
|
+
callnext=callnext,
|
|
243
|
+
callenter=callenter, # type: ignore
|
|
244
|
+
callexit=callexit, # type: ignore
|
|
245
|
+
)
|
|
122
246
|
if not callable(callprev):
|
|
123
247
|
callprev = None
|
|
124
248
|
if not callable(callnext):
|
|
125
249
|
callnext = None
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
callenter
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if
|
|
250
|
+
def gen():
|
|
251
|
+
try:
|
|
252
|
+
if callable(callenter):
|
|
253
|
+
callenter(iterable)
|
|
254
|
+
for e in iterable:
|
|
255
|
+
if callprev:
|
|
256
|
+
try:
|
|
257
|
+
callprev(e)
|
|
258
|
+
except (StopIteration, GeneratorExit):
|
|
259
|
+
break
|
|
260
|
+
yield e
|
|
261
|
+
if callnext:
|
|
262
|
+
try:
|
|
263
|
+
callnext(e)
|
|
264
|
+
except (StopIteration, GeneratorExit):
|
|
265
|
+
break
|
|
266
|
+
except BaseException as e:
|
|
267
|
+
if callable(callexit):
|
|
268
|
+
if not callexit(iterable, e):
|
|
269
|
+
raise
|
|
270
|
+
else:
|
|
144
271
|
raise
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
callexit(it, None)
|
|
272
|
+
finally:
|
|
273
|
+
if callable(callexit):
|
|
274
|
+
callexit(iterable, None)
|
|
275
|
+
return gen()
|
|
150
276
|
|
|
151
277
|
|
|
152
|
-
async def wrap_aiter(
|
|
153
|
-
|
|
278
|
+
async def wrap_aiter[T](
|
|
279
|
+
iterable: Iterable[T] | AsyncIterable[T],
|
|
154
280
|
/,
|
|
155
281
|
callprev: None | Callable[[T], Any] = None,
|
|
156
282
|
callnext: None | Callable[[T], Any] = None,
|
|
@@ -161,7 +287,7 @@ async def wrap_aiter(
|
|
|
161
287
|
callprev = ensure_async(callprev, threaded=threaded) if callable(callprev) else None
|
|
162
288
|
callnext = ensure_async(callnext, threaded=threaded) if callable(callnext) else None
|
|
163
289
|
try:
|
|
164
|
-
async for e in ensure_aiter(
|
|
290
|
+
async for e in ensure_aiter(iterable, threaded=threaded):
|
|
165
291
|
if callprev:
|
|
166
292
|
try:
|
|
167
293
|
await callprev(e)
|
|
@@ -175,13 +301,13 @@ async def wrap_aiter(
|
|
|
175
301
|
break
|
|
176
302
|
except BaseException as e:
|
|
177
303
|
if callable(callexit):
|
|
178
|
-
if not await ensure_async(callexit, threaded=threaded)(
|
|
304
|
+
if not await ensure_async(callexit, threaded=threaded)(iterable, e):
|
|
179
305
|
raise
|
|
180
306
|
else:
|
|
181
307
|
raise
|
|
182
308
|
finally:
|
|
183
309
|
if callable(callexit):
|
|
184
|
-
await ensure_async(callexit, threaded=threaded)(
|
|
310
|
+
await ensure_async(callexit, threaded=threaded)(iterable, None)
|
|
185
311
|
|
|
186
312
|
|
|
187
313
|
def acc_step(
|
|
@@ -210,7 +336,7 @@ def cut_iter(
|
|
|
210
336
|
yield stop, stop - start
|
|
211
337
|
|
|
212
338
|
|
|
213
|
-
def run_gen_step(
|
|
339
|
+
def run_gen_step[T](
|
|
214
340
|
gen_step: Generator[Any, Any, T] | Callable[[], Generator[Any, Any, T]],
|
|
215
341
|
*,
|
|
216
342
|
async_: bool = False,
|
|
@@ -272,16 +398,16 @@ def run_gen_step(
|
|
|
272
398
|
result = process()
|
|
273
399
|
if as_iter:
|
|
274
400
|
async def wrap(result):
|
|
275
|
-
|
|
401
|
+
iterable = await result
|
|
276
402
|
try:
|
|
277
|
-
|
|
403
|
+
iterable = aiter(iterable)
|
|
278
404
|
except TypeError:
|
|
279
|
-
for val in iter(
|
|
405
|
+
for val in iter(iterable):
|
|
280
406
|
if isawaitable(val):
|
|
281
407
|
val = await val
|
|
282
408
|
yield val
|
|
283
409
|
else:
|
|
284
|
-
async for val in
|
|
410
|
+
async for val in iterable:
|
|
285
411
|
yield val
|
|
286
412
|
result = wrap(result)
|
|
287
413
|
return result
|
|
@@ -440,3 +566,45 @@ def run_gen_step_iter(
|
|
|
440
566
|
close()
|
|
441
567
|
return process()
|
|
442
568
|
|
|
569
|
+
|
|
570
|
+
@overload
|
|
571
|
+
def bfs_gen[T](
|
|
572
|
+
initial: T,
|
|
573
|
+
/,
|
|
574
|
+
unpack_iterator: Literal[False] = False,
|
|
575
|
+
) -> Generator[T, T | None, None]:
|
|
576
|
+
...
|
|
577
|
+
@overload
|
|
578
|
+
def bfs_gen[T](
|
|
579
|
+
initial: T | Iterator[T],
|
|
580
|
+
/,
|
|
581
|
+
unpack_iterator: Literal[True],
|
|
582
|
+
) -> Generator[T, T | None, None]:
|
|
583
|
+
...
|
|
584
|
+
def bfs_gen[T](
|
|
585
|
+
initial: T | Iterator[T],
|
|
586
|
+
/,
|
|
587
|
+
unpack_iterator: bool = False,
|
|
588
|
+
) -> Generator[T, T | None, None]:
|
|
589
|
+
"""辅助函数,返回生成器,用来简化广度优先遍历
|
|
590
|
+
"""
|
|
591
|
+
dq: deque[T] = deque()
|
|
592
|
+
push, pushmany, pop = dq.append, dq.extend, dq.popleft
|
|
593
|
+
if isinstance(initial, Iterator) and unpack_iterator:
|
|
594
|
+
pushmany(initial)
|
|
595
|
+
else:
|
|
596
|
+
push(initial) # type: ignore
|
|
597
|
+
while dq:
|
|
598
|
+
args: None | T = yield (val := pop())
|
|
599
|
+
if unpack_iterator:
|
|
600
|
+
while args is not None:
|
|
601
|
+
if isinstance(args, Iterator):
|
|
602
|
+
pushmany(args)
|
|
603
|
+
else:
|
|
604
|
+
push(args)
|
|
605
|
+
args = yield val
|
|
606
|
+
else:
|
|
607
|
+
while args is not None:
|
|
608
|
+
push(args)
|
|
609
|
+
args = yield val
|
|
610
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-iterutils"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
description = "Python another itertools."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -13,7 +13,7 @@ classifiers = [
|
|
|
13
13
|
"Development Status :: 5 - Production/Stable",
|
|
14
14
|
"Programming Language :: Python",
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"Programming Language :: Python :: 3.
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
17
|
"Programming Language :: Python :: 3 :: Only",
|
|
18
18
|
"Operating System :: OS Independent",
|
|
19
19
|
"Intended Audience :: Developers",
|
|
@@ -26,8 +26,8 @@ include = [
|
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
|
-
python = "^3.
|
|
30
|
-
python-asynctools = ">=0.0.
|
|
29
|
+
python = "^3.12"
|
|
30
|
+
python-asynctools = ">=0.0.9"
|
|
31
31
|
|
|
32
32
|
[build-system]
|
|
33
33
|
requires = ["poetry-core"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|