python-iterutils 0.2.4__tar.gz → 0.2.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-iterutils
3
- Version: 0.2.4
3
+ Version: 0.2.5
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,7 +20,7 @@ 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.1.2)
23
+ Requires-Dist: python-asynctools (>=0.1.3.4)
24
24
  Requires-Dist: python-texttools (>=0.0.3)
25
25
  Requires-Dist: python-undefined (>=0.0.3)
26
26
  Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-iterutils
@@ -2,12 +2,12 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 2, 4)
5
+ __version__ = (0, 2, 5)
6
6
  __all__ = [
7
7
  "Yield", "YieldFrom", "iterable", "async_iterable",
8
8
  "run_gen_step", "run_gen_step_iter", "as_gen_step",
9
9
  "as_gen_step_iter", "split_cm, ""with_iter_next",
10
- "map", "filter", "reduce", "zip", "chunked",
10
+ "map", "filter", "reduce", "zip", "chain", "chunked",
11
11
  "foreach", "async_foreach", "through", "async_through",
12
12
  "flatten", "async_flatten", "collect", "async_collect",
13
13
  "group_collect", "async_group_collect", "iter_unique",
@@ -30,7 +30,7 @@ from contextlib import (
30
30
  )
31
31
  from copy import copy
32
32
  from dataclasses import dataclass
33
- from itertools import batched, pairwise
33
+ from itertools import batched, chain as _chain, pairwise
34
34
  from inspect import isawaitable, iscoroutinefunction, signature
35
35
  from sys import _getframe, exc_info
36
36
  from _thread import start_new_thread
@@ -42,7 +42,7 @@ from typing import (
42
42
 
43
43
  from asynctools import (
44
44
  async_filter, async_map, async_reduce, async_zip, async_batched,
45
- ensure_async, ensure_aiter, collect as async_collect,
45
+ ensure_async, ensure_aiter, async_chain, collect as async_collect,
46
46
  )
47
47
  from texttools import format_time
48
48
  from undefined import undefined
@@ -85,7 +85,8 @@ def _get_async(back: int = 2, /) -> bool:
85
85
  return f_locals.get("async_") or False
86
86
  while f_locals is not None and f_locals is not f_globals:
87
87
  if "async_" in f_locals:
88
- return f_locals["async_"] or False
88
+ if f_locals["async_"] is not None:
89
+ return f_locals["async_"]
89
90
  f = f.f_back
90
91
  if f is None:
91
92
  break
@@ -270,8 +271,12 @@ def as_gen_step[**Args](
270
271
  /,
271
272
  ) -> Callable[Args, Any]:
272
273
  def wrapper(*args: Args.args, **kwds: Args.kwargs):
273
- async_ = cast(None | Literal[False, True], kwds.pop("async_", None))
274
- return run_gen_step(gen_step, async_, *args, **kwds)
274
+ return run_gen_step(
275
+ gen_step,
276
+ cast(None | Literal[False, True], kwds.pop("async_", None)),
277
+ *args,
278
+ **kwds,
279
+ )
275
280
  return wrapper
276
281
 
277
282
 
@@ -280,8 +285,12 @@ def as_gen_step_iter[**Args](
280
285
  /,
281
286
  ) -> Callable[Args, Iterable | AsyncIterable]:
282
287
  def wrapper(*args: Args.args, **kwds: Args.kwargs):
283
- async_ = cast(None | Literal[False, True], kwds.pop("async_", None))
284
- return run_gen_step_iter(gen_step, async_, *args, **kwds)
288
+ return run_gen_step_iter(
289
+ gen_step,
290
+ cast(None | Literal[False, True], kwds.pop("async_", None)),
291
+ *args,
292
+ **kwds,
293
+ )
285
294
  return wrapper
286
295
 
287
296
 
@@ -329,7 +338,7 @@ def split_cm[T](
329
338
  finally:
330
339
  yield exit()
331
340
 
332
- run_gen_step(gen_step, async_=async_)
341
+ run_gen_step(gen_step, async_)
333
342
  """
334
343
  if isinstance(cm, AbstractAsyncContextManager):
335
344
  enter: Callable = cm.__aenter__
@@ -380,7 +389,7 @@ def with_iter_next[T](
380
389
  e = yield do_next()
381
390
  do_what_you_want()
382
391
 
383
- run_gen_step(gen_step, async_=async_)
392
+ run_gen_step(gen_step, async_)
384
393
  """
385
394
  if isinstance(iterable, AsyncIterable):
386
395
  try:
@@ -399,17 +408,21 @@ def map(
399
408
  iterable: Iterable | AsyncIterable,
400
409
  /,
401
410
  *iterables: Iterable | AsyncIterable,
411
+ threaded: bool = False,
402
412
  ):
403
413
  """
404
414
  """
405
415
  if (
416
+ threaded or
406
417
  iscoroutinefunction(function) or
407
418
  isinstance(iterable, AsyncIterable) or
408
419
  any(isinstance(i, AsyncIterable) for i in iterables)
409
420
  ):
410
421
  if function is None:
411
422
  if iterables:
412
- return async_zip(iterable, *iterables)
423
+ return async_zip(iterable, *iterables, threaded=threaded)
424
+ elif threaded:
425
+ return ensure_aiter(iterable, threaded=threaded)
413
426
  else:
414
427
  return iterable
415
428
  return async_map(function, iterable, *iterables)
@@ -425,11 +438,12 @@ def filter(
425
438
  function: None | Callable,
426
439
  iterable: Iterable | AsyncIterable,
427
440
  /,
441
+ threaded: bool = False,
428
442
  ):
429
443
  """
430
444
  """
431
- if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
432
- return async_filter(function, iterable)
445
+ if threaded or iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
446
+ return async_filter(function, iterable, threaded=threaded)
433
447
  return _filter(function, iterable)
434
448
 
435
449
 
@@ -438,11 +452,12 @@ def reduce(
438
452
  iterable: Iterable | AsyncIterable,
439
453
  initial: Any = undefined,
440
454
  /,
455
+ threaded: bool = False,
441
456
  ):
442
457
  """
443
458
  """
444
- if iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
445
- return async_reduce(function, iterable, initial)
459
+ if threaded or iscoroutinefunction(function) or isinstance(iterable, AsyncIterable):
460
+ return async_reduce(function, iterable, initial, threaded=threaded)
446
461
  from functools import reduce
447
462
  if initial is undefined:
448
463
  return reduce(function, iterable)
@@ -453,19 +468,78 @@ def zip(
453
468
  iterable: Iterable | AsyncIterable,
454
469
  /,
455
470
  *iterables: Iterable | AsyncIterable,
471
+ threaded: bool = False,
456
472
  ):
457
473
  """
458
474
  """
459
- if isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
460
- return async_zip(iterable, *iterables)
475
+ if threaded or isinstance(iterable, AsyncIterable) or any(isinstance(i, AsyncIterable) for i in iterables):
476
+ return async_zip(iterable, *iterables, threaded=threaded)
461
477
  return _zip(iterable, *iterables)
462
478
 
463
479
 
480
+ @overload
481
+ def chain[T](
482
+ iterable: Iterable[T],
483
+ /,
484
+ *iterables: Iterable[T],
485
+ threaded: bool = False,
486
+ ) -> Iterator[T]:
487
+ ...
488
+ @overload
489
+ def chain[T](
490
+ iterable: AsyncIterable[T],
491
+ /,
492
+ *iterables: Iterable[T] | AsyncIterable[T],
493
+ threaded: bool = False,
494
+ ) -> AsyncIterator[T]:
495
+ ...
496
+ def chain[T](
497
+ iterable: Iterable[T] | AsyncIterable[T],
498
+ /,
499
+ *iterables: Iterable[T] | AsyncIterable[T],
500
+ threaded: bool = False,
501
+ ) -> Iterator[T] | AsyncIterator[T]:
502
+ if threaded or not isinstance(iterable, Iterable):
503
+ return async_chain(iterable, *iterables, threaded=threaded)
504
+ return _chain(iterable, *iterables) # type: ignore
505
+
506
+
507
+ @overload
508
+ def chain_from_iterable[T](
509
+ iterables: Iterable[Iterable[T]],
510
+ threaded: Literal[False] = False,
511
+ *,
512
+ async_: Literal[False] = False,
513
+ ) -> Iterator[T]:
514
+ ...
515
+ @overload
516
+ def chain_from_iterable[T](
517
+ iterables: Iterable[Iterable[T] | AsyncIterable[T]] | AsyncIterable[Iterable[T] | AsyncIterable[T]],
518
+ threaded: bool = False,
519
+ *,
520
+ async_: Literal[True],
521
+ ) -> AsyncIterator[T]:
522
+ ...
523
+ def chain_from_iterable[T](
524
+ iterables: Iterable[Iterable[T]] | Iterable[Iterable[T] | AsyncIterable[T]] | AsyncIterable[Iterable[T] | AsyncIterable[T]],
525
+ threaded: bool = False,
526
+ *,
527
+ async_: Literal[False, True] = False,
528
+ ) -> Iterator[T] | AsyncIterator[T]:
529
+ if async_ or threaded or not isinstance(iterables, Iterable):
530
+ if isinstance(iterables, Iterable):
531
+ return async_chain.from_iterable(iterables, threaded=threaded)
532
+ else:
533
+ return async_chain.from_iterable(iterables, threaded=threaded)
534
+ return _chain.from_iterable(iterables) # type: ignore
535
+
536
+
464
537
  @overload
465
538
  def chunked[T](
466
539
  iterable: Iterable[T],
467
540
  n: int = 1,
468
541
  /,
542
+ threaded: bool = False,
469
543
  ) -> Iterator[Sequence[T]]:
470
544
  ...
471
545
  @overload
@@ -473,12 +547,14 @@ def chunked[T](
473
547
  iterable: AsyncIterable[T],
474
548
  n: int = 1,
475
549
  /,
550
+ threaded: bool = False,
476
551
  ) -> AsyncIterator[Sequence[T]]:
477
552
  ...
478
553
  def chunked[T](
479
554
  iterable: Iterable[T] | AsyncIterable[T],
480
555
  n: int = 1,
481
556
  /,
557
+ threaded: bool = False,
482
558
  ) -> Iterator[Sequence[T]] | AsyncIterator[Sequence[T]]:
483
559
  """
484
560
  """
@@ -488,10 +564,10 @@ def chunked[T](
488
564
  if n == 1:
489
565
  return ((e,) for e in iterable)
490
566
  return (iterable[i:j] for i, j in pairwise(range(0, len(iterable)+n, n)))
491
- elif isinstance(iterable, Iterable):
567
+ elif not threaded and isinstance(iterable, Iterable):
492
568
  return batched(iterable, n)
493
569
  else:
494
- return async_batched(iterable, n)
570
+ return async_batched(iterable, n, threaded=threaded)
495
571
 
496
572
 
497
573
  def foreach(
@@ -499,11 +575,12 @@ def foreach(
499
575
  iterable: Iterable | AsyncIterable,
500
576
  /,
501
577
  *iterables: Iterable | AsyncIterable,
578
+ threaded: bool = False,
502
579
  ):
503
580
  """
504
581
  """
505
- if not (isinstance(iterable, Iterable) and all(isinstance(it, Iterable) for it in iterables)):
506
- return async_foreach(value, iterable, *iterables)
582
+ if threaded or not (isinstance(iterable, Iterable) and all(isinstance(it, Iterable) for it in iterables)):
583
+ return async_foreach(value, iterable, *iterables, threaded=threaded)
507
584
  if iterables:
508
585
  for args in _zip(iterable, *iterables):
509
586
  value(*args)
@@ -534,11 +611,12 @@ def through(
534
611
  iterable: Iterable | AsyncIterable,
535
612
  /,
536
613
  take_while: None | Callable = None,
614
+ threaded: bool = False,
537
615
  ):
538
616
  """
539
617
  """
540
- if not isinstance(iterable, Iterable):
541
- return async_through(iterable, take_while)
618
+ if threaded or not isinstance(iterable, Iterable):
619
+ return async_through(iterable, take_while, threaded=threaded)
542
620
  if take_while is None:
543
621
  for _ in iterable:
544
622
  pass
@@ -575,6 +653,7 @@ def flatten(
575
653
  iterable: Iterable,
576
654
  /,
577
655
  exclude_types: type | tuple[type, ...] = (Buffer, str),
656
+ threaded: bool = False,
578
657
  ) -> Iterator:
579
658
  ...
580
659
  @overload
@@ -582,17 +661,19 @@ def flatten(
582
661
  iterable: AsyncIterable,
583
662
  /,
584
663
  exclude_types: type | tuple[type, ...] = (Buffer, str),
664
+ threaded: bool = False,
585
665
  ) -> AsyncIterator:
586
666
  ...
587
667
  def flatten(
588
668
  iterable: Iterable | AsyncIterable,
589
669
  /,
590
670
  exclude_types: type | tuple[type, ...] = (Buffer, str),
671
+ threaded: bool = False,
591
672
  ) -> Iterator | AsyncIterator:
592
673
  """
593
674
  """
594
- if not isinstance(iterable, Iterable):
595
- return async_flatten(iterable, exclude_types)
675
+ if threaded or not isinstance(iterable, Iterable):
676
+ return async_flatten(iterable, exclude_types, threaded=threaded)
596
677
  def gen(iterable):
597
678
  for e in iterable:
598
679
  if isinstance(e, (Iterable, AsyncIterable)) and not isinstance(e, exclude_types):
@@ -623,6 +704,7 @@ def collect[K, V](
623
704
  iterable: Iterable[tuple[K, V]] | Mapping[K, V],
624
705
  /,
625
706
  rettype: Callable[[Iterable[tuple[K, V]]], MutableMapping[K, V]],
707
+ threaded: bool = False,
626
708
  ) -> MutableMapping[K, V]:
627
709
  ...
628
710
  @overload
@@ -630,6 +712,7 @@ def collect[T](
630
712
  iterable: Iterable[T],
631
713
  /,
632
714
  rettype: Callable[[Iterable[T]], Collection[T]] = list,
715
+ threaded: bool = False,
633
716
  ) -> Collection[T]:
634
717
  ...
635
718
  @overload
@@ -637,6 +720,7 @@ def collect[K, V](
637
720
  iterable: AsyncIterable[tuple[K, V]],
638
721
  /,
639
722
  rettype: Callable[[Iterable[tuple[K, V]]], MutableMapping[K, V]],
723
+ threaded: bool = False,
640
724
  ) -> Coroutine[Any, Any, MutableMapping[K, V]]:
641
725
  ...
642
726
  @overload
@@ -644,17 +728,19 @@ def collect[T](
644
728
  iterable: AsyncIterable[T],
645
729
  /,
646
730
  rettype: Callable[[Iterable[T]], Collection[T]] = list,
731
+ threaded: bool = False,
647
732
  ) -> Coroutine[Any, Any, Collection[T]]:
648
733
  ...
649
734
  def collect(
650
735
  iterable: Iterable | AsyncIterable | Mapping,
651
736
  /,
652
737
  rettype: Callable[[Iterable], Collection] = list,
738
+ threaded: bool = False,
653
739
  ) -> Collection | Coroutine[Any, Any, Collection]:
654
740
  """
655
741
  """
656
- if not isinstance(iterable, Iterable):
657
- return async_collect(iterable, rettype)
742
+ if threaded or not isinstance(iterable, Iterable):
743
+ return async_collect(iterable, rettype, threaded=threaded)
658
744
  return rettype(iterable)
659
745
 
660
746
 
@@ -663,6 +749,7 @@ def group_collect[K, V, C: Container](
663
749
  iterable: Iterable[tuple[K, V]],
664
750
  mapping: None = None,
665
751
  factory: None | C | Callable[[], C] = None,
752
+ threaded: bool = False,
666
753
  ) -> dict[K, C]:
667
754
  ...
668
755
  @overload
@@ -670,6 +757,7 @@ def group_collect[K, V, C: Container, M: MutableMapping](
670
757
  iterable: Iterable[tuple[K, V]],
671
758
  mapping: M,
672
759
  factory: None | C | Callable[[], C] = None,
760
+ threaded: bool = False,
673
761
  ) -> M:
674
762
  ...
675
763
  @overload
@@ -677,6 +765,7 @@ def group_collect[K, V, C: Container](
677
765
  iterable: AsyncIterable[tuple[K, V]],
678
766
  mapping: None = None,
679
767
  factory: None | C | Callable[[], C] = None,
768
+ threaded: bool = False,
680
769
  ) -> Coroutine[Any, Any, dict[K, C]]:
681
770
  ...
682
771
  @overload
@@ -684,17 +773,19 @@ def group_collect[K, V, C: Container, M: MutableMapping](
684
773
  iterable: AsyncIterable[tuple[K, V]],
685
774
  mapping: M,
686
775
  factory: None | C | Callable[[], C] = None,
776
+ threaded: bool = False,
687
777
  ) -> Coroutine[Any, Any, M]:
688
778
  ...
689
779
  def group_collect[K, V, C: Container, M: MutableMapping](
690
780
  iterable: Iterable[tuple[K, V]] | AsyncIterable[tuple[K, V]],
691
781
  mapping: None | M = None,
692
782
  factory: None | C | Callable[[], C] = None,
783
+ threaded: bool = False,
693
784
  ) -> dict[K, C] | M | Coroutine[Any, Any, dict[K, C]] | Coroutine[Any, Any, M]:
694
785
  """
695
786
  """
696
- if not isinstance(iterable, Iterable):
697
- return async_group_collect(iterable, mapping, factory)
787
+ if threaded or not isinstance(iterable, Iterable):
788
+ return async_group_collect(iterable, mapping, factory, threaded=threaded)
698
789
  if factory is None:
699
790
  if isinstance(mapping, defaultdict):
700
791
  factory = mapping.default_factory
@@ -791,6 +882,7 @@ def iter_unique[T](
791
882
  iterable: Iterable[T],
792
883
  /,
793
884
  seen: None | MutableSet = None,
885
+ threaded: bool = False,
794
886
  ) -> Iterator[T]:
795
887
  ...
796
888
  @overload
@@ -798,17 +890,19 @@ def iter_unique[T](
798
890
  iterable: AsyncIterable[T],
799
891
  /,
800
892
  seen: None | MutableSet = None,
893
+ threaded: bool = False,
801
894
  ) -> AsyncIterator[T]:
802
895
  ...
803
896
  def iter_unique[T](
804
897
  iterable: Iterable[T] | AsyncIterable[T],
805
898
  /,
806
899
  seen: None | MutableSet = None,
900
+ threaded: bool = False,
807
901
  ) -> Iterator[T] | AsyncIterator[T]:
808
902
  """
809
903
  """
810
- if not isinstance(iterable, Iterable):
811
- return async_iter_unique(iterable, seen)
904
+ if threaded or not isinstance(iterable, Iterable):
905
+ return async_iter_unique(iterable, seen, threaded=threaded)
812
906
  if seen is None:
813
907
  seen = set()
814
908
  def gen(iterable):
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-iterutils"
3
- version = "0.2.4"
3
+ version = "0.2.5"
4
4
  description = "Python another itertools."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
@@ -27,7 +27,7 @@ include = [
27
27
 
28
28
  [tool.poetry.dependencies]
29
29
  python = "^3.12"
30
- python-asynctools = ">=0.1.2"
30
+ python-asynctools = ">=0.1.3.4"
31
31
  python-texttools = ">=0.0.3"
32
32
  python-undefined = ">=0.0.3"
33
33