python-iterutils 0.1.6.1__py3-none-any.whl → 0.1.8__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,33 +2,38 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 1, 6)
5
+ __version__ = (0, 1, 8)
6
6
  __all__ = [
7
7
  "Return", "Yield", "YieldFrom", "iterable", "async_iterable", "foreach", "async_foreach",
8
- "through", "async_through", "flatten", "async_flatten", "map", "filter", "reduce", "zip",
9
- "chunked", "iter_unique", "async_iter_unique", "wrap_iter", "wrap_aiter", "acc_step",
10
- "cut_iter", "run_gen_step", "run_gen_step_iter", "as_gen_step", "bfs_gen", "with_iter_next",
11
- "backgroud_loop",
8
+ "through", "async_through", "flatten", "async_flatten", "collect", "async_collect",
9
+ "group_collect", "async_group_collect", "map", "filter", "reduce", "zip", "chunked",
10
+ "iter_unique", "async_iter_unique", "wrap_iter", "wrap_aiter", "acc_step", "cut_iter",
11
+ "run_gen_step", "run_gen_step_iter", "as_gen_step", "bfs_gen", "with_iter_next", "backgroud_loop",
12
12
  ]
13
13
 
14
14
  from abc import ABC, abstractmethod
15
15
  from asyncio import create_task, sleep as async_sleep, to_thread
16
16
  from builtins import map as _map, filter as _filter, zip as _zip
17
- from collections import deque
17
+ from collections import defaultdict, deque
18
18
  from collections.abc import (
19
- AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable, Coroutine, Generator,
20
- Iterable, Iterator, MutableSet, Sequence,
19
+ AsyncIterable, AsyncIterator, Awaitable, Buffer, Callable, Collection, Container,
20
+ Coroutine, Generator, Iterable, Iterator, Mapping, MutableMapping, MutableSet,
21
+ MutableSequence, Sequence, ValuesView,
21
22
  )
22
23
  from contextlib import asynccontextmanager, contextmanager, ExitStack, AsyncExitStack
24
+ from copy import copy
23
25
  from dataclasses import dataclass
24
26
  from itertools import batched, pairwise
25
27
  from inspect import isawaitable, iscoroutinefunction
28
+ from sys import _getframe
26
29
  from _thread import start_new_thread
27
30
  from time import sleep, time
28
- from typing import overload, Any, AsyncContextManager, ContextManager, Literal
31
+ from types import FrameType
32
+ from typing import cast, overload, Any, AsyncContextManager, ContextManager, Literal
29
33
 
30
34
  from asynctools import (
31
35
  async_filter, async_map, async_reduce, async_zip, async_batched, ensure_async, ensure_aiter,
36
+ collect as async_collect,
32
37
  )
33
38
  from decotools import optional
34
39
  from texttools import format_time
@@ -94,7 +99,7 @@ async def async_foreach(
94
99
  iterable: Iterable | AsyncIterable,
95
100
  /,
96
101
  *iterables: Iterable | AsyncIterable,
97
- threaded: bool = True,
102
+ threaded: bool = False,
98
103
  ):
99
104
  value = ensure_async(value, threaded=threaded)
100
105
  if iterables:
@@ -125,7 +130,7 @@ async def async_through(
125
130
  iterable: Iterable | AsyncIterable,
126
131
  /,
127
132
  take_while: None | Callable = None,
128
- threaded: bool = True,
133
+ threaded: bool = False,
129
134
  ):
130
135
  iterable = ensure_aiter(iterable, threaded=threaded)
131
136
  if take_while is None:
@@ -175,7 +180,7 @@ async def async_flatten(
175
180
  iterable: Iterable | AsyncIterable,
176
181
  /,
177
182
  exclude_types: type | tuple[type, ...] = (Buffer, str),
178
- threaded: bool = True,
183
+ threaded: bool = False,
179
184
  ) -> AsyncIterator:
180
185
  async for e in ensure_aiter(iterable, threaded=threaded):
181
186
  if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
@@ -185,6 +190,168 @@ async def async_flatten(
185
190
  yield e
186
191
 
187
192
 
193
+ @overload
194
+ def collect[K, V](
195
+ iterable: Iterable[tuple[K, V]] | Mapping[K, V],
196
+ /,
197
+ rettype: Callable[[Iterable[tuple[K, V]]], MutableMapping[K, V]],
198
+ ) -> MutableMapping[K, V]:
199
+ ...
200
+ @overload
201
+ def collect[T](
202
+ iterable: Iterable[T],
203
+ /,
204
+ rettype: Callable[[Iterable[T]], Collection[T]] = list,
205
+ ) -> Collection[T]:
206
+ ...
207
+ @overload
208
+ def collect[K, V](
209
+ iterable: AsyncIterable[tuple[K, V]],
210
+ /,
211
+ rettype: Callable[[Iterable[tuple[K, V]]], MutableMapping[K, V]],
212
+ ) -> Coroutine[Any, Any, MutableMapping[K, V]]:
213
+ ...
214
+ @overload
215
+ def collect[T](
216
+ iterable: AsyncIterable[T],
217
+ /,
218
+ rettype: Callable[[Iterable[T]], Collection[T]] = list,
219
+ ) -> Coroutine[Any, Any, Collection[T]]:
220
+ ...
221
+ def collect(
222
+ iterable: Iterable | AsyncIterable | Mapping,
223
+ /,
224
+ rettype: Callable[[Iterable], Collection] = list,
225
+ ) -> Collection | Coroutine[Any, Any, Collection]:
226
+ if not isinstance(iterable, Iterable):
227
+ return async_collect(iterable, rettype)
228
+ return rettype(iterable)
229
+
230
+
231
+ @overload
232
+ def group_collect[K, V, C: Container](
233
+ iterable: Iterable[tuple[K, V]],
234
+ mapping: None = None,
235
+ factory: None | C | Callable[[], C] = None,
236
+ ) -> dict[K, C]:
237
+ ...
238
+ @overload
239
+ def group_collect[K, V, C: Container, M: MutableMapping](
240
+ iterable: Iterable[tuple[K, V]],
241
+ mapping: M,
242
+ factory: None | C | Callable[[], C] = None,
243
+ ) -> M:
244
+ ...
245
+ @overload
246
+ def group_collect[K, V, C: Container](
247
+ iterable: AsyncIterable[tuple[K, V]],
248
+ mapping: None = None,
249
+ factory: None | C | Callable[[], C] = None,
250
+ ) -> Coroutine[Any, Any, dict[K, C]]:
251
+ ...
252
+ @overload
253
+ def group_collect[K, V, C: Container, M: MutableMapping](
254
+ iterable: AsyncIterable[tuple[K, V]],
255
+ mapping: M,
256
+ factory: None | C | Callable[[], C] = None,
257
+ ) -> Coroutine[Any, Any, M]:
258
+ ...
259
+ def group_collect[K, V, C: Container, M: MutableMapping](
260
+ iterable: Iterable[tuple[K, V]] | AsyncIterable[tuple[K, V]],
261
+ mapping: None | M = None,
262
+ factory: None | C | Callable[[], C] = None,
263
+ ) -> dict[K, C] | M | Coroutine[Any, Any, dict[K, C]] | Coroutine[Any, Any, M]:
264
+ if not isinstance(iterable, Iterable):
265
+ return async_group_collect(iterable, mapping, factory)
266
+ if factory is None:
267
+ if isinstance(mapping, defaultdict):
268
+ factory = mapping.default_factory
269
+ elif mapping:
270
+ factory = type(next(iter(ValuesView(mapping))))
271
+ else:
272
+ factory = cast(type[C], list)
273
+ elif callable(factory):
274
+ pass
275
+ elif isinstance(factory, Container):
276
+ factory = cast(Callable[[], C], lambda _obj=factory: copy(_obj))
277
+ else:
278
+ raise ValueError("can't determine factory")
279
+ factory = cast(Callable[[], C], factory)
280
+ if isinstance(factory, type):
281
+ factory_type = factory
282
+ else:
283
+ factory_type = type(factory())
284
+ if issubclass(factory_type, MutableSequence):
285
+ add = getattr(factory_type, "append")
286
+ else:
287
+ add = getattr(factory_type, "add")
288
+ if mapping is None:
289
+ mapping = cast(M, {})
290
+ for k, v in iterable:
291
+ try:
292
+ c = mapping[k]
293
+ except LookupError:
294
+ c = mapping[k] = factory()
295
+ add(c, v)
296
+ return mapping
297
+
298
+
299
+ @overload
300
+ async def async_group_collect[K, V, C: Container](
301
+ iterable: Iterable[tuple[K, V]] | AsyncIterable[tuple[K, V]],
302
+ mapping: None = None,
303
+ factory: None | C | Callable[[], C] = None,
304
+ threaded: bool = False,
305
+ ) -> dict[K, C]:
306
+ ...
307
+ @overload
308
+ async def async_group_collect[K, V, C: Container, M: MutableMapping](
309
+ iterable: Iterable[tuple[K, V]] | AsyncIterable[tuple[K, V]],
310
+ mapping: M,
311
+ factory: None | C | Callable[[], C] = None,
312
+ threaded: bool = False,
313
+ ) -> M:
314
+ ...
315
+ async def async_group_collect[K, V, C: Container, M: MutableMapping](
316
+ iterable: Iterable[tuple[K, V]] | AsyncIterable[tuple[K, V]],
317
+ mapping: None | M = None,
318
+ factory: None | C | Callable[[], C] = None,
319
+ threaded: bool = False,
320
+ ) -> dict[K, C] | M:
321
+ iterable = ensure_aiter(iterable, threaded=threaded)
322
+ if factory is None:
323
+ if isinstance(mapping, defaultdict):
324
+ factory = mapping.default_factory
325
+ elif mapping:
326
+ factory = type(next(iter(ValuesView(mapping))))
327
+ else:
328
+ factory = cast(type[C], list)
329
+ elif callable(factory):
330
+ pass
331
+ elif isinstance(factory, Container):
332
+ factory = cast(Callable[[], C], lambda _obj=factory: copy(_obj))
333
+ else:
334
+ raise ValueError("can't determine factory")
335
+ factory = cast(Callable[[], C], factory)
336
+ if isinstance(factory, type):
337
+ factory_type = factory
338
+ else:
339
+ factory_type = type(factory())
340
+ if issubclass(factory_type, MutableSequence):
341
+ add = getattr(factory_type, "append")
342
+ else:
343
+ add = getattr(factory_type, "add")
344
+ if mapping is None:
345
+ mapping = cast(M, {})
346
+ async for k, v in iterable:
347
+ try:
348
+ c = mapping[k]
349
+ except LookupError:
350
+ c = mapping[k] = factory()
351
+ add(c, v)
352
+ return mapping
353
+
354
+
188
355
  def map(
189
356
  function: None | Callable,
190
357
  iterable: Iterable | AsyncIterable,
@@ -398,7 +565,7 @@ async def wrap_aiter[T](
398
565
  callnext: None | Callable[[T], Any] = None,
399
566
  callenter: None | Callable[[Iterable[T] | AsyncIterable[T]], Any] = None,
400
567
  callexit: None | Callable[[Iterable[T] | AsyncIterable[T], None | BaseException], Any] = None,
401
- threaded: bool = True,
568
+ threaded: bool = False,
402
569
  ) -> AsyncIterator[T]:
403
570
  callprev = ensure_async(callprev, threaded=threaded) if callable(callprev) else None
404
571
  callnext = ensure_async(callnext, threaded=threaded) if callable(callnext) else None
@@ -452,13 +619,32 @@ def cut_iter(
452
619
  yield stop, stop - start
453
620
 
454
621
 
622
+ def _get_async(back: int = 2) -> bool:
623
+ f: None | FrameType
624
+ f = _getframe(back)
625
+ f_globals = f.f_globals
626
+ f_locals = f.f_locals
627
+ if f_locals is f_globals:
628
+ return f_locals.get("async_") or False
629
+ while f_locals is not f_globals:
630
+ if "async_" in f_locals:
631
+ return f_locals["async_"] or False
632
+ f = f.f_back
633
+ if f is None:
634
+ break
635
+ f_locals = f.f_locals
636
+ return False
637
+
638
+
455
639
  def run_gen_step[T](
456
640
  gen_step: Generator[Any, Any, T] | Callable[[], Generator[Any, Any, T]],
457
641
  *,
458
- async_: bool = False,
642
+ async_: None | bool = None,
459
643
  threaded: bool = False,
460
644
  as_iter: bool = False,
461
645
  ) -> T:
646
+ if async_ is None:
647
+ async_ = _get_async()
462
648
  if callable(gen_step):
463
649
  gen = gen_step()
464
650
  close = gen.close
@@ -561,7 +747,15 @@ def run_gen_step_iter(
561
747
  gen_step: Generator | Callable[[], Generator],
562
748
  threaded: bool = False,
563
749
  *,
564
- async_: Literal[False] = False,
750
+ async_: None = None,
751
+ ) -> Iterator | AsyncIterator:
752
+ ...
753
+ @overload
754
+ def run_gen_step_iter(
755
+ gen_step: Generator | Callable[[], Generator],
756
+ threaded: bool = False,
757
+ *,
758
+ async_: Literal[False],
565
759
  ) -> Iterator:
566
760
  ...
567
761
  @overload
@@ -576,8 +770,10 @@ def run_gen_step_iter(
576
770
  gen_step: Generator | Callable[[], Generator],
577
771
  threaded: bool = False,
578
772
  *,
579
- async_: bool = False,
773
+ async_: None | bool = None,
580
774
  ) -> Iterator | AsyncIterator:
775
+ if async_ is None:
776
+ async_ = _get_async()
581
777
  if callable(gen_step):
582
778
  gen = gen_step()
583
779
  close = gen.close
@@ -687,9 +883,11 @@ def run_gen_step_iter(
687
883
  def as_gen_step(
688
884
  func: Callable,
689
885
  /,
690
- async_: bool = False,
886
+ async_: None | bool = None,
691
887
  iter: bool = False,
692
888
  ) -> Callable:
889
+ if async_ is None:
890
+ async_ = _get_async()
693
891
  def wrapper(*args, **kwds):
694
892
  if iter:
695
893
  return run_gen_step_iter(func(*args, **kwds), async_=async_) # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.1.6.1
3
+ Version: 0.1.8
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
@@ -20,8 +20,8 @@ Classifier: Programming Language :: Python :: 3 :: Only
20
20
  Classifier: Topic :: Software Development
21
21
  Classifier: Topic :: Software Development :: Libraries
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Requires-Dist: python-asynctools (>=0.0.10)
24
- Requires-Dist: python-decotools (>=0.0.1.2)
23
+ Requires-Dist: python-asynctools (>=0.0.10.1)
24
+ Requires-Dist: python-decotools (>=0.0.2)
25
25
  Requires-Dist: python-texttools (>=0.0.3)
26
26
  Requires-Dist: python-undefined (>=0.0.3)
27
27
  Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-iterutils
@@ -0,0 +1,7 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ iterutils/__init__.py,sha256=zcNPBE2QtJZyGlTr4n913Zwtd2OJAiAFdvEaYIur0kw,32751
3
+ iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ python_iterutils-0.1.8.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
+ python_iterutils-0.1.8.dist-info/METADATA,sha256=JuLFHrGXyrqYv-ajoNOpWuxXKAhAu9CMch5Mbscvf3E,1470
6
+ python_iterutils-0.1.8.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
+ python_iterutils-0.1.8.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
- iterutils/__init__.py,sha256=q5UPgJ2fSDIMQIRFyKlgLqWDiy-b_G8Ft6BSFz_lH3Y,26588
3
- iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- python_iterutils-0.1.6.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
- python_iterutils-0.1.6.1.dist-info/METADATA,sha256=k7dUrZEwrlFiXjqon1V7-1KxPeoh0a5UcAcy502VAR4,1472
6
- python_iterutils-0.1.6.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
- python_iterutils-0.1.6.1.dist-info/RECORD,,