dycw-utilities 0.133.6__py3-none-any.whl → 0.134.0__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.
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/RECORD +39 -39
- utilities/__init__.py +1 -1
- utilities/arq.py +13 -20
- utilities/asyncio.py +59 -74
- utilities/atools.py +10 -13
- utilities/cachetools.py +11 -17
- utilities/click.py +61 -55
- utilities/concurrent.py +7 -10
- utilities/dataclasses.py +69 -91
- utilities/enum.py +24 -21
- utilities/eventkit.py +34 -48
- utilities/functions.py +133 -168
- utilities/functools.py +14 -18
- utilities/hypothesis.py +34 -44
- utilities/iterables.py +165 -179
- utilities/luigi.py +3 -15
- utilities/memory_profiler.py +11 -15
- utilities/more_itertools.py +85 -94
- utilities/operator.py +5 -7
- utilities/optuna.py +6 -6
- utilities/pathlib.py +1 -0
- utilities/period.py +7 -9
- utilities/polars.py +5 -16
- utilities/pqdm.py +7 -8
- utilities/pydantic.py +2 -4
- utilities/pytest.py +14 -23
- utilities/python_dotenv.py +5 -9
- utilities/random.py +2 -3
- utilities/redis.py +163 -181
- utilities/slack_sdk.py +2 -2
- utilities/sqlalchemy.py +4 -14
- utilities/timer.py +6 -0
- utilities/typed_settings.py +7 -10
- utilities/types.py +10 -94
- utilities/typing.py +32 -43
- utilities/uuid.py +1 -0
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/licenses/LICENSE +0 -0
utilities/iterables.py
CHANGED
@@ -22,10 +22,8 @@ from operator import add, itemgetter, or_
|
|
22
22
|
from typing import (
|
23
23
|
TYPE_CHECKING,
|
24
24
|
Any,
|
25
|
-
Generic,
|
26
25
|
Literal,
|
27
26
|
TypeGuard,
|
28
|
-
TypeVar,
|
29
27
|
assert_never,
|
30
28
|
cast,
|
31
29
|
overload,
|
@@ -43,46 +41,34 @@ from utilities.math import (
|
|
43
41
|
)
|
44
42
|
from utilities.reprlib import get_repr
|
45
43
|
from utilities.sentinel import Sentinel, sentinel
|
46
|
-
from utilities.types import
|
44
|
+
from utilities.types import SupportsAdd, SupportsLT
|
47
45
|
|
48
46
|
if TYPE_CHECKING:
|
49
47
|
from types import NoneType
|
50
48
|
|
51
|
-
from utilities.types import MaybeIterable, MaybeIterableHashable, StrMapping
|
52
|
-
|
53
|
-
|
54
|
-
_K = TypeVar("_K")
|
55
|
-
_T = TypeVar("_T")
|
56
|
-
_U = TypeVar("_U")
|
57
|
-
_V = TypeVar("_V")
|
58
|
-
_W = TypeVar("_W")
|
59
|
-
_T1 = TypeVar("_T1")
|
60
|
-
_T2 = TypeVar("_T2")
|
61
|
-
_T3 = TypeVar("_T3")
|
62
|
-
_T4 = TypeVar("_T4")
|
63
|
-
_T5 = TypeVar("_T5")
|
49
|
+
from utilities.types import MaybeIterable, MaybeIterableHashable, Sign, StrMapping
|
64
50
|
|
65
51
|
|
66
52
|
##
|
67
53
|
|
68
54
|
|
69
|
-
def always_iterable(obj: MaybeIterable[
|
55
|
+
def always_iterable[T](obj: MaybeIterable[T], /) -> Iterable[T]:
|
70
56
|
"""Typed version of `always_iterable`."""
|
71
57
|
obj = cast("Any", obj)
|
72
58
|
if isinstance(obj, str | bytes):
|
73
|
-
return cast("list[
|
59
|
+
return cast("list[T]", [obj])
|
74
60
|
try:
|
75
|
-
return iter(cast("Iterable[
|
61
|
+
return iter(cast("Iterable[T]", obj))
|
76
62
|
except TypeError:
|
77
|
-
return cast("list[
|
63
|
+
return cast("list[T]", [obj])
|
78
64
|
|
79
65
|
|
80
66
|
##
|
81
67
|
|
82
68
|
|
83
|
-
def always_iterable_hashable(
|
84
|
-
obj: MaybeIterable[
|
85
|
-
) -> MaybeIterableHashable[
|
69
|
+
def always_iterable_hashable[T](
|
70
|
+
obj: MaybeIterable[T] | None, /
|
71
|
+
) -> MaybeIterableHashable[T] | None:
|
86
72
|
"""Ensure an object is always hashable."""
|
87
73
|
return None if obj is None else tuple(always_iterable(obj))
|
88
74
|
|
@@ -90,9 +76,9 @@ def always_iterable_hashable(
|
|
90
76
|
##
|
91
77
|
|
92
78
|
|
93
|
-
def apply_bijection(
|
94
|
-
func: Callable[[
|
95
|
-
) -> Mapping[
|
79
|
+
def apply_bijection[T, U](
|
80
|
+
func: Callable[[T], U], iterable: Iterable[T], /
|
81
|
+
) -> Mapping[T, U]:
|
96
82
|
"""Apply a function bijectively."""
|
97
83
|
keys = list(iterable)
|
98
84
|
try:
|
@@ -112,21 +98,21 @@ def apply_bijection(
|
|
112
98
|
|
113
99
|
|
114
100
|
@dataclass(kw_only=True, slots=True)
|
115
|
-
class ApplyBijectionError(Exception
|
116
|
-
keys: list[
|
117
|
-
counts: Mapping[
|
101
|
+
class ApplyBijectionError[T](Exception):
|
102
|
+
keys: list[T]
|
103
|
+
counts: Mapping[T, int]
|
118
104
|
|
119
105
|
|
120
106
|
@dataclass(kw_only=True, slots=True)
|
121
|
-
class _ApplyBijectionDuplicateKeysError(ApplyBijectionError[
|
107
|
+
class _ApplyBijectionDuplicateKeysError[T](ApplyBijectionError[T]):
|
122
108
|
@override
|
123
109
|
def __str__(self) -> str:
|
124
110
|
return f"Keys {get_repr(self.keys)} must not contain duplicates; got {get_repr(self.counts)}"
|
125
111
|
|
126
112
|
|
127
113
|
@dataclass(kw_only=True, slots=True)
|
128
|
-
class _ApplyBijectionDuplicateValuesError
|
129
|
-
values: list[
|
114
|
+
class _ApplyBijectionDuplicateValuesError[T, U](ApplyBijectionError[T]):
|
115
|
+
values: list[U]
|
130
116
|
|
131
117
|
@override
|
132
118
|
def __str__(self) -> str:
|
@@ -136,7 +122,7 @@ class _ApplyBijectionDuplicateValuesError(ApplyBijectionError[_T], Generic[_T, _
|
|
136
122
|
##
|
137
123
|
|
138
124
|
|
139
|
-
def apply_to_tuple(func: Callable[...,
|
125
|
+
def apply_to_tuple[T](func: Callable[..., T], args: tuple[Any, ...], /) -> T:
|
140
126
|
"""Apply a function to a tuple of args."""
|
141
127
|
return apply_to_varargs(func, *args)
|
142
128
|
|
@@ -144,7 +130,7 @@ def apply_to_tuple(func: Callable[..., _T], args: tuple[Any, ...], /) -> _T:
|
|
144
130
|
##
|
145
131
|
|
146
132
|
|
147
|
-
def apply_to_varargs(func: Callable[...,
|
133
|
+
def apply_to_varargs[T](func: Callable[..., T], *args: Any) -> T:
|
148
134
|
"""Apply a function to a variable number of arguments."""
|
149
135
|
return func(*args)
|
150
136
|
|
@@ -153,17 +139,17 @@ def apply_to_varargs(func: Callable[..., _T], *args: Any) -> _T:
|
|
153
139
|
|
154
140
|
|
155
141
|
@overload
|
156
|
-
def chain_mappings(
|
157
|
-
*mappings: Mapping[
|
158
|
-
) -> Mapping[
|
142
|
+
def chain_mappings[K, V](
|
143
|
+
*mappings: Mapping[K, V], list: Literal[True]
|
144
|
+
) -> Mapping[K, Sequence[V]]: ...
|
159
145
|
@overload
|
160
|
-
def chain_mappings(
|
161
|
-
*mappings: Mapping[
|
162
|
-
) -> Mapping[
|
163
|
-
def chain_mappings(
|
164
|
-
*mappings: Mapping[
|
146
|
+
def chain_mappings[K, V](
|
147
|
+
*mappings: Mapping[K, V], list: bool = False
|
148
|
+
) -> Mapping[K, Iterable[V]]: ...
|
149
|
+
def chain_mappings[K, V](
|
150
|
+
*mappings: Mapping[K, V],
|
165
151
|
list: bool = False, # noqa: A002
|
166
|
-
) -> Mapping[
|
152
|
+
) -> Mapping[K, Iterable[V]]:
|
167
153
|
"""Chain the values of a set of mappings."""
|
168
154
|
try:
|
169
155
|
first, *rest = mappings
|
@@ -176,9 +162,9 @@ def chain_mappings(
|
|
176
162
|
return reduced
|
177
163
|
|
178
164
|
|
179
|
-
def _chain_mappings_one(
|
180
|
-
acc: Mapping[
|
181
|
-
) -> Mapping[
|
165
|
+
def _chain_mappings_one[K, V](
|
166
|
+
acc: Mapping[K, Iterable[V]], el: Mapping[K, V], /
|
167
|
+
) -> Mapping[K, Iterable[V]]:
|
182
168
|
"""Chain the values of a set of mappings."""
|
183
169
|
out = dict(acc)
|
184
170
|
for key, value in el.items():
|
@@ -189,7 +175,7 @@ def _chain_mappings_one(
|
|
189
175
|
##
|
190
176
|
|
191
177
|
|
192
|
-
def chain_maybe_iterables(*maybe_iterables: MaybeIterable[
|
178
|
+
def chain_maybe_iterables[T](*maybe_iterables: MaybeIterable[T]) -> Iterable[T]:
|
193
179
|
"""Chain a set of maybe iterables."""
|
194
180
|
iterables = map(always_iterable, maybe_iterables)
|
195
181
|
return chain.from_iterable(iterables)
|
@@ -198,7 +184,7 @@ def chain_maybe_iterables(*maybe_iterables: MaybeIterable[_T]) -> Iterable[_T]:
|
|
198
184
|
##
|
199
185
|
|
200
186
|
|
201
|
-
def chain_nullable(*maybe_iterables: Iterable[
|
187
|
+
def chain_nullable[T](*maybe_iterables: Iterable[T | None] | None) -> Iterable[T]:
|
202
188
|
"""Chain a set of values; ignoring nulls."""
|
203
189
|
iterables = (mi for mi in maybe_iterables if mi is not None)
|
204
190
|
values = ((i for i in it if i is not None) for it in iterables)
|
@@ -217,7 +203,7 @@ def check_bijection(mapping: Mapping[Any, Hashable], /) -> None:
|
|
217
203
|
|
218
204
|
|
219
205
|
@dataclass(kw_only=True, slots=True)
|
220
|
-
class CheckBijectionError
|
206
|
+
class CheckBijectionError[THashable](Exception):
|
221
207
|
mapping: Mapping[Any, THashable]
|
222
208
|
counts: Mapping[THashable, int]
|
223
209
|
|
@@ -237,7 +223,7 @@ def check_duplicates(iterable: Iterable[Hashable], /) -> None:
|
|
237
223
|
|
238
224
|
|
239
225
|
@dataclass(kw_only=True, slots=True)
|
240
|
-
class CheckDuplicatesError
|
226
|
+
class CheckDuplicatesError[THashable](Exception):
|
241
227
|
iterable: Iterable[THashable]
|
242
228
|
counts: Mapping[THashable, int]
|
243
229
|
|
@@ -280,10 +266,10 @@ type _CheckIterablesEqualState = Literal["left_longer", "right_longer"]
|
|
280
266
|
|
281
267
|
|
282
268
|
@dataclass(kw_only=True, slots=True)
|
283
|
-
class CheckIterablesEqualError(Exception
|
284
|
-
left: list[
|
285
|
-
right: list[
|
286
|
-
errors: list[tuple[int,
|
269
|
+
class CheckIterablesEqualError[T](Exception):
|
270
|
+
left: list[T]
|
271
|
+
right: list[T]
|
272
|
+
errors: list[tuple[int, T, T]]
|
287
273
|
state: _CheckIterablesEqualState | None
|
288
274
|
|
289
275
|
@override
|
@@ -434,12 +420,12 @@ def check_mappings_equal(left: Mapping[Any, Any], right: Mapping[Any, Any], /) -
|
|
434
420
|
|
435
421
|
|
436
422
|
@dataclass(kw_only=True, slots=True)
|
437
|
-
class CheckMappingsEqualError
|
438
|
-
left: Mapping[
|
439
|
-
right: Mapping[
|
440
|
-
left_extra: AbstractSet[
|
441
|
-
right_extra: AbstractSet[
|
442
|
-
errors: list[tuple[
|
423
|
+
class CheckMappingsEqualError[K, V](Exception):
|
424
|
+
left: Mapping[K, V]
|
425
|
+
right: Mapping[K, V]
|
426
|
+
left_extra: AbstractSet[K]
|
427
|
+
right_extra: AbstractSet[K]
|
428
|
+
errors: list[tuple[K, V, V]]
|
443
429
|
|
444
430
|
@override
|
445
431
|
def __str__(self) -> str:
|
@@ -484,11 +470,11 @@ def check_sets_equal(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
484
470
|
|
485
471
|
|
486
472
|
@dataclass(kw_only=True, slots=True)
|
487
|
-
class CheckSetsEqualError(Exception
|
488
|
-
left: AbstractSet[
|
489
|
-
right: AbstractSet[
|
490
|
-
left_extra: AbstractSet[
|
491
|
-
right_extra: AbstractSet[
|
473
|
+
class CheckSetsEqualError[T](Exception):
|
474
|
+
left: AbstractSet[T]
|
475
|
+
right: AbstractSet[T]
|
476
|
+
left_extra: AbstractSet[T]
|
477
|
+
right_extra: AbstractSet[T]
|
492
478
|
|
493
479
|
@override
|
494
480
|
def __str__(self) -> str:
|
@@ -531,11 +517,11 @@ def check_submapping(left: Mapping[Any, Any], right: Mapping[Any, Any], /) -> No
|
|
531
517
|
|
532
518
|
|
533
519
|
@dataclass(kw_only=True, slots=True)
|
534
|
-
class CheckSubMappingError
|
535
|
-
left: Mapping[
|
536
|
-
right: Mapping[
|
537
|
-
extra: AbstractSet[
|
538
|
-
errors: list[tuple[
|
520
|
+
class CheckSubMappingError[K, V](Exception):
|
521
|
+
left: Mapping[K, V]
|
522
|
+
right: Mapping[K, V]
|
523
|
+
extra: AbstractSet[K]
|
524
|
+
errors: list[tuple[K, V, V]]
|
539
525
|
|
540
526
|
@override
|
541
527
|
def __str__(self) -> str:
|
@@ -570,10 +556,10 @@ def check_subset(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
570
556
|
|
571
557
|
|
572
558
|
@dataclass(kw_only=True, slots=True)
|
573
|
-
class CheckSubSetError(Exception
|
574
|
-
left: AbstractSet[
|
575
|
-
right: AbstractSet[
|
576
|
-
extra: AbstractSet[
|
559
|
+
class CheckSubSetError[T](Exception):
|
560
|
+
left: AbstractSet[T]
|
561
|
+
right: AbstractSet[T]
|
562
|
+
extra: AbstractSet[T]
|
577
563
|
|
578
564
|
@override
|
579
565
|
def __str__(self) -> str:
|
@@ -602,11 +588,11 @@ def check_supermapping(left: Mapping[Any, Any], right: Mapping[Any, Any], /) ->
|
|
602
588
|
|
603
589
|
|
604
590
|
@dataclass(kw_only=True, slots=True)
|
605
|
-
class CheckSuperMappingError
|
606
|
-
left: Mapping[
|
607
|
-
right: Mapping[
|
608
|
-
extra: AbstractSet[
|
609
|
-
errors: list[tuple[
|
591
|
+
class CheckSuperMappingError[K, V](Exception):
|
592
|
+
left: Mapping[K, V]
|
593
|
+
right: Mapping[K, V]
|
594
|
+
extra: AbstractSet[K]
|
595
|
+
errors: list[tuple[K, V, V]]
|
610
596
|
|
611
597
|
@override
|
612
598
|
def __str__(self) -> str:
|
@@ -641,10 +627,10 @@ def check_superset(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
641
627
|
|
642
628
|
|
643
629
|
@dataclass(kw_only=True, slots=True)
|
644
|
-
class CheckSuperSetError(Exception
|
645
|
-
left: AbstractSet[
|
646
|
-
right: AbstractSet[
|
647
|
-
extra: AbstractSet[
|
630
|
+
class CheckSuperSetError[T](Exception):
|
631
|
+
left: AbstractSet[T]
|
632
|
+
right: AbstractSet[T]
|
633
|
+
extra: AbstractSet[T]
|
648
634
|
|
649
635
|
@override
|
650
636
|
def __str__(self) -> str:
|
@@ -693,7 +679,7 @@ class _CheckUniqueModuloCaseDuplicateLowerCaseStringsError(CheckUniqueModuloCase
|
|
693
679
|
##
|
694
680
|
|
695
681
|
|
696
|
-
def cmp_nullable(x:
|
682
|
+
def cmp_nullable[T: SupportsLT](x: T | None, y: T | None, /) -> Sign:
|
697
683
|
"""Compare two nullable objects."""
|
698
684
|
match x, y:
|
699
685
|
case None, None:
|
@@ -711,7 +697,7 @@ def cmp_nullable(x: TSupportsLT | None, y: TSupportsLT | None, /) -> Sign:
|
|
711
697
|
##
|
712
698
|
|
713
699
|
|
714
|
-
def chunked(iterable: Iterable[
|
700
|
+
def chunked[T](iterable: Iterable[T], n: int, /) -> Iterator[Sequence[T]]:
|
715
701
|
"""Break an iterable into lists of length n."""
|
716
702
|
return iter(partial(take, n, iter(iterable)), [])
|
717
703
|
|
@@ -769,10 +755,10 @@ class EnsureIterableNotStrError(Exception):
|
|
769
755
|
##
|
770
756
|
|
771
757
|
|
772
|
-
def expanding_window(iterable: Iterable[
|
758
|
+
def expanding_window[T](iterable: Iterable[T], /) -> islice[list[T]]:
|
773
759
|
"""Yield an expanding window over an iterable."""
|
774
760
|
|
775
|
-
def func(acc: Iterable[
|
761
|
+
def func(acc: Iterable[T], el: T, /) -> list[T]:
|
776
762
|
return list(chain(acc, [el]))
|
777
763
|
|
778
764
|
return islice(accumulate(iterable, func=func, initial=[]), 1, None)
|
@@ -782,31 +768,31 @@ def expanding_window(iterable: Iterable[_T], /) -> islice[list[_T]]:
|
|
782
768
|
|
783
769
|
|
784
770
|
@overload
|
785
|
-
def filter_include_and_exclude(
|
786
|
-
iterable: Iterable[
|
771
|
+
def filter_include_and_exclude[T, U](
|
772
|
+
iterable: Iterable[T],
|
787
773
|
/,
|
788
774
|
*,
|
789
|
-
include: MaybeIterable[
|
790
|
-
exclude: MaybeIterable[
|
791
|
-
key: Callable[[
|
792
|
-
) -> Iterable[
|
775
|
+
include: MaybeIterable[U] | None = None,
|
776
|
+
exclude: MaybeIterable[U] | None = None,
|
777
|
+
key: Callable[[T], U],
|
778
|
+
) -> Iterable[T]: ...
|
793
779
|
@overload
|
794
|
-
def filter_include_and_exclude(
|
795
|
-
iterable: Iterable[
|
780
|
+
def filter_include_and_exclude[T](
|
781
|
+
iterable: Iterable[T],
|
796
782
|
/,
|
797
783
|
*,
|
798
|
-
include: MaybeIterable[
|
799
|
-
exclude: MaybeIterable[
|
800
|
-
key: Callable[[
|
801
|
-
) -> Iterable[
|
802
|
-
def filter_include_and_exclude(
|
803
|
-
iterable: Iterable[
|
784
|
+
include: MaybeIterable[T] | None = None,
|
785
|
+
exclude: MaybeIterable[T] | None = None,
|
786
|
+
key: Callable[[T], Any] | None = None,
|
787
|
+
) -> Iterable[T]: ...
|
788
|
+
def filter_include_and_exclude[T, U](
|
789
|
+
iterable: Iterable[T],
|
804
790
|
/,
|
805
791
|
*,
|
806
|
-
include: MaybeIterable[
|
807
|
-
exclude: MaybeIterable[
|
808
|
-
key: Callable[[
|
809
|
-
) -> Iterable[
|
792
|
+
include: MaybeIterable[U] | None = None,
|
793
|
+
exclude: MaybeIterable[U] | None = None,
|
794
|
+
key: Callable[[T], U] | None = None,
|
795
|
+
) -> Iterable[T]:
|
810
796
|
"""Filter an iterable based on an inclusion/exclusion pair."""
|
811
797
|
include, exclude = resolve_include_and_exclude(include=include, exclude=exclude)
|
812
798
|
if include is not None:
|
@@ -844,16 +830,16 @@ def ungroup_consecutive_integers(
|
|
844
830
|
|
845
831
|
|
846
832
|
@overload
|
847
|
-
def groupby_lists(
|
848
|
-
iterable: Iterable[
|
849
|
-
) -> Iterator[tuple[
|
833
|
+
def groupby_lists[T](
|
834
|
+
iterable: Iterable[T], /, *, key: None = None
|
835
|
+
) -> Iterator[tuple[T, list[T]]]: ...
|
850
836
|
@overload
|
851
|
-
def groupby_lists(
|
852
|
-
iterable: Iterable[
|
853
|
-
) -> Iterator[tuple[
|
854
|
-
def groupby_lists(
|
855
|
-
iterable: Iterable[
|
856
|
-
) -> Iterator[tuple[
|
837
|
+
def groupby_lists[T, U](
|
838
|
+
iterable: Iterable[T], /, *, key: Callable[[T], U]
|
839
|
+
) -> Iterator[tuple[U, list[T]]]: ...
|
840
|
+
def groupby_lists[T, U](
|
841
|
+
iterable: Iterable[T], /, *, key: Callable[[T], U] | None = None
|
842
|
+
) -> Iterator[tuple[T, list[T]]] | Iterator[tuple[U, list[T]]]:
|
857
843
|
"""Yield consecutive keys and groups (as lists)."""
|
858
844
|
if key is None:
|
859
845
|
for k, group in groupby(iterable):
|
@@ -866,7 +852,7 @@ def groupby_lists(
|
|
866
852
|
##
|
867
853
|
|
868
854
|
|
869
|
-
def hashable_to_iterable(obj:
|
855
|
+
def hashable_to_iterable[T: Hashable](obj: T | None, /) -> tuple[T, ...] | None:
|
870
856
|
"""Lift a hashable singleton to an iterable of hashables."""
|
871
857
|
return None if obj is None else (obj,)
|
872
858
|
|
@@ -902,9 +888,9 @@ def is_iterable_not_str(obj: Any, /) -> TypeGuard[Iterable[Any]]:
|
|
902
888
|
##
|
903
889
|
|
904
890
|
|
905
|
-
def map_mapping(
|
906
|
-
func: Callable[[
|
907
|
-
) -> Mapping[
|
891
|
+
def map_mapping[K, V, W](
|
892
|
+
func: Callable[[V], W], mapping: Mapping[K, V], /
|
893
|
+
) -> Mapping[K, W]:
|
908
894
|
"""Map a function over the values of a mapping."""
|
909
895
|
return {k: func(v) for k, v in mapping.items()}
|
910
896
|
|
@@ -912,7 +898,7 @@ def map_mapping(
|
|
912
898
|
##
|
913
899
|
|
914
900
|
|
915
|
-
def merge_mappings(*mappings: Mapping[
|
901
|
+
def merge_mappings[K, V](*mappings: Mapping[K, V]) -> Mapping[K, V]:
|
916
902
|
"""Merge a set of mappings."""
|
917
903
|
return reduce(or_, map(dict, mappings), {})
|
918
904
|
|
@@ -920,7 +906,7 @@ def merge_mappings(*mappings: Mapping[_K, _V]) -> Mapping[_K, _V]:
|
|
920
906
|
##
|
921
907
|
|
922
908
|
|
923
|
-
def merge_sets(*iterables: Iterable[
|
909
|
+
def merge_sets[T](*iterables: Iterable[T]) -> AbstractSet[T]:
|
924
910
|
"""Merge a set of sets."""
|
925
911
|
return reduce(or_, map(set, iterables), set())
|
926
912
|
|
@@ -967,7 +953,7 @@ class MergeStrMappingsError(Exception):
|
|
967
953
|
##
|
968
954
|
|
969
955
|
|
970
|
-
def one(*iterables: Iterable[
|
956
|
+
def one[T](*iterables: Iterable[T]) -> T:
|
971
957
|
"""Return the unique value in a set of iterables."""
|
972
958
|
it = iter(chain(*iterables))
|
973
959
|
try:
|
@@ -982,21 +968,21 @@ def one(*iterables: Iterable[_T]) -> _T:
|
|
982
968
|
|
983
969
|
|
984
970
|
@dataclass(kw_only=True, slots=True)
|
985
|
-
class OneError(Exception
|
986
|
-
iterables: tuple[Iterable[
|
971
|
+
class OneError[T](Exception):
|
972
|
+
iterables: tuple[Iterable[T], ...]
|
987
973
|
|
988
974
|
|
989
975
|
@dataclass(kw_only=True, slots=True)
|
990
|
-
class OneEmptyError(OneError[
|
976
|
+
class OneEmptyError[T](OneError[T]):
|
991
977
|
@override
|
992
978
|
def __str__(self) -> str:
|
993
979
|
return f"Iterable(s) {get_repr(self.iterables)} must not be empty"
|
994
980
|
|
995
981
|
|
996
982
|
@dataclass(kw_only=True, slots=True)
|
997
|
-
class OneNonUniqueError(OneError
|
998
|
-
first:
|
999
|
-
second:
|
983
|
+
class OneNonUniqueError[T](OneError):
|
984
|
+
first: T
|
985
|
+
second: T
|
1000
986
|
|
1001
987
|
@override
|
1002
988
|
def __str__(self) -> str:
|
@@ -1006,7 +992,7 @@ class OneNonUniqueError(OneError, Generic[_T]):
|
|
1006
992
|
##
|
1007
993
|
|
1008
994
|
|
1009
|
-
def one_maybe(*objs: MaybeIterable[
|
995
|
+
def one_maybe[T](*objs: MaybeIterable[T]) -> T:
|
1010
996
|
"""Return the unique value in a set of values/iterables."""
|
1011
997
|
try:
|
1012
998
|
return one(chain_maybe_iterables(*objs))
|
@@ -1030,10 +1016,10 @@ class OneMaybeEmptyError(OneMaybeError):
|
|
1030
1016
|
|
1031
1017
|
|
1032
1018
|
@dataclass(kw_only=True, slots=True)
|
1033
|
-
class OneMaybeNonUniqueError(OneMaybeError
|
1034
|
-
objs: tuple[MaybeIterable[
|
1035
|
-
first:
|
1036
|
-
second:
|
1019
|
+
class OneMaybeNonUniqueError[T](OneMaybeError):
|
1020
|
+
objs: tuple[MaybeIterable[T], ...]
|
1021
|
+
first: T
|
1022
|
+
second: T
|
1037
1023
|
|
1038
1024
|
@override
|
1039
1025
|
def __str__(self) -> str:
|
@@ -1133,7 +1119,7 @@ class OneStrNonUniqueError(OneStrError):
|
|
1133
1119
|
##
|
1134
1120
|
|
1135
1121
|
|
1136
|
-
def one_unique(*iterables: Iterable[
|
1122
|
+
def one_unique[T: Hashable](*iterables: Iterable[T]) -> T:
|
1137
1123
|
"""Return the set-unique value in a set of iterables."""
|
1138
1124
|
try:
|
1139
1125
|
return one(set(chain(*iterables)))
|
@@ -1157,7 +1143,7 @@ class OneUniqueEmptyError(OneUniqueError):
|
|
1157
1143
|
|
1158
1144
|
|
1159
1145
|
@dataclass(kw_only=True, slots=True)
|
1160
|
-
class OneUniqueNonUniqueError
|
1146
|
+
class OneUniqueNonUniqueError[THashable](OneUniqueError):
|
1161
1147
|
iterables: tuple[MaybeIterable[THashable], ...]
|
1162
1148
|
first: THashable
|
1163
1149
|
second: THashable
|
@@ -1170,7 +1156,7 @@ class OneUniqueNonUniqueError(OneUniqueError, Generic[THashable]):
|
|
1170
1156
|
##
|
1171
1157
|
|
1172
1158
|
|
1173
|
-
def pairwise_tail(iterable: Iterable[
|
1159
|
+
def pairwise_tail[T](iterable: Iterable[T], /) -> Iterator[tuple[T, T | Sentinel]]:
|
1174
1160
|
"""Return pairwise elements, with the last paired with the sentinel."""
|
1175
1161
|
return pairwise(chain(iterable, [sentinel]))
|
1176
1162
|
|
@@ -1178,11 +1164,11 @@ def pairwise_tail(iterable: Iterable[_T], /) -> Iterator[tuple[_T, _T | Sentinel
|
|
1178
1164
|
##
|
1179
1165
|
|
1180
1166
|
|
1181
|
-
def product_dicts(mapping: Mapping[
|
1167
|
+
def product_dicts[K, V](mapping: Mapping[K, Iterable[V]], /) -> Iterator[Mapping[K, V]]:
|
1182
1168
|
"""Return the cartesian product of the values in a mapping, as mappings."""
|
1183
1169
|
keys = list(mapping)
|
1184
1170
|
for values in product(*mapping.values()):
|
1185
|
-
yield cast("Mapping[
|
1171
|
+
yield cast("Mapping[K, V]", dict(zip(keys, values, strict=True)))
|
1186
1172
|
|
1187
1173
|
|
1188
1174
|
##
|
@@ -1239,41 +1225,39 @@ class _RangePartitionsNumError(RangePartitionsError):
|
|
1239
1225
|
|
1240
1226
|
|
1241
1227
|
@overload
|
1242
|
-
def reduce_mappings(
|
1243
|
-
func: Callable[[
|
1244
|
-
) -> Mapping[
|
1228
|
+
def reduce_mappings[K, V](
|
1229
|
+
func: Callable[[V, V], V], sequence: Iterable[Mapping[K, V]], /
|
1230
|
+
) -> Mapping[K, V]: ...
|
1245
1231
|
@overload
|
1246
|
-
def reduce_mappings(
|
1247
|
-
func: Callable[[
|
1248
|
-
sequence: Iterable[Mapping[
|
1232
|
+
def reduce_mappings[K, V, W](
|
1233
|
+
func: Callable[[W, V], W],
|
1234
|
+
sequence: Iterable[Mapping[K, V]],
|
1249
1235
|
/,
|
1250
1236
|
*,
|
1251
|
-
initial:
|
1252
|
-
) -> Mapping[
|
1253
|
-
def reduce_mappings(
|
1254
|
-
func: Callable[[
|
1255
|
-
sequence: Iterable[Mapping[
|
1237
|
+
initial: W | Sentinel = sentinel,
|
1238
|
+
) -> Mapping[K, W]: ...
|
1239
|
+
def reduce_mappings[K, V, W](
|
1240
|
+
func: Callable[[V, V], V] | Callable[[W, V], W],
|
1241
|
+
sequence: Iterable[Mapping[K, V]],
|
1256
1242
|
/,
|
1257
1243
|
*,
|
1258
|
-
initial:
|
1259
|
-
) -> Mapping[
|
1244
|
+
initial: W | Sentinel = sentinel,
|
1245
|
+
) -> Mapping[K, V | W]:
|
1260
1246
|
"""Reduce a function over the values of a set of mappings."""
|
1261
1247
|
chained = chain_mappings(*sequence)
|
1262
1248
|
if isinstance(initial, Sentinel):
|
1263
|
-
func2 = cast("Callable[[
|
1249
|
+
func2 = cast("Callable[[V, V], V]", func)
|
1264
1250
|
return {k: reduce(func2, v) for k, v in chained.items()}
|
1265
|
-
func2 = cast("Callable[[
|
1251
|
+
func2 = cast("Callable[[W, V], W]", func)
|
1266
1252
|
return {k: reduce(func2, v, initial) for k, v in chained.items()}
|
1267
1253
|
|
1268
1254
|
|
1269
1255
|
##
|
1270
1256
|
|
1271
1257
|
|
1272
|
-
def resolve_include_and_exclude(
|
1273
|
-
*,
|
1274
|
-
|
1275
|
-
exclude: MaybeIterable[_T] | None = None,
|
1276
|
-
) -> tuple[set[_T] | None, set[_T] | None]:
|
1258
|
+
def resolve_include_and_exclude[T](
|
1259
|
+
*, include: MaybeIterable[T] | None = None, exclude: MaybeIterable[T] | None = None
|
1260
|
+
) -> tuple[set[T] | None, set[T] | None]:
|
1277
1261
|
"""Resolve an inclusion/exclusion pair."""
|
1278
1262
|
include_use = include if include is None else set(always_iterable(include))
|
1279
1263
|
exclude_use = exclude if exclude is None else set(always_iterable(exclude))
|
@@ -1287,9 +1271,9 @@ def resolve_include_and_exclude(
|
|
1287
1271
|
|
1288
1272
|
|
1289
1273
|
@dataclass(kw_only=True, slots=True)
|
1290
|
-
class ResolveIncludeAndExcludeError(Exception
|
1291
|
-
include: Iterable[
|
1292
|
-
exclude: Iterable[
|
1274
|
+
class ResolveIncludeAndExcludeError[T](Exception):
|
1275
|
+
include: Iterable[T]
|
1276
|
+
exclude: Iterable[T]
|
1293
1277
|
|
1294
1278
|
@override
|
1295
1279
|
def __str__(self) -> str:
|
@@ -1302,7 +1286,7 @@ class ResolveIncludeAndExcludeError(Exception, Generic[_T]):
|
|
1302
1286
|
##
|
1303
1287
|
|
1304
1288
|
|
1305
|
-
def sort_iterable(iterable: Iterable[
|
1289
|
+
def sort_iterable[T](iterable: Iterable[T], /) -> list[T]:
|
1306
1290
|
"""Sort an iterable across types."""
|
1307
1291
|
return sorted(iterable, key=cmp_to_key(_sort_iterable_cmp))
|
1308
1292
|
|
@@ -1385,7 +1369,9 @@ def _sort_iterable_cmp_floats(x: float, y: float, /) -> Sign:
|
|
1385
1369
|
##
|
1386
1370
|
|
1387
1371
|
|
1388
|
-
def sum_mappings
|
1372
|
+
def sum_mappings[K: Hashable, V: SupportsAdd](
|
1373
|
+
*mappings: Mapping[K, V],
|
1374
|
+
) -> Mapping[K, V]:
|
1389
1375
|
"""Sum the values of a set of mappings."""
|
1390
1376
|
return reduce_mappings(add, mappings, initial=0)
|
1391
1377
|
|
@@ -1393,7 +1379,7 @@ def sum_mappings(*mappings: Mapping[_K, TSupportsAdd]) -> Mapping[_K, TSupportsA
|
|
1393
1379
|
##
|
1394
1380
|
|
1395
1381
|
|
1396
|
-
def take(n: int, iterable: Iterable[
|
1382
|
+
def take[T](n: int, iterable: Iterable[T], /) -> Sequence[T]:
|
1397
1383
|
"""Return first n items of the iterable as a list."""
|
1398
1384
|
return list(islice(iterable, n))
|
1399
1385
|
|
@@ -1402,23 +1388,23 @@ def take(n: int, iterable: Iterable[_T], /) -> Sequence[_T]:
|
|
1402
1388
|
|
1403
1389
|
|
1404
1390
|
@overload
|
1405
|
-
def transpose(iterable: Iterable[tuple[
|
1391
|
+
def transpose[T1](iterable: Iterable[tuple[T1]], /) -> tuple[list[T1]]: ...
|
1406
1392
|
@overload
|
1407
|
-
def transpose(
|
1408
|
-
iterable: Iterable[tuple[
|
1409
|
-
) -> tuple[list[
|
1393
|
+
def transpose[T1, T2](
|
1394
|
+
iterable: Iterable[tuple[T1, T2]], /
|
1395
|
+
) -> tuple[list[T1], list[T2]]: ...
|
1410
1396
|
@overload
|
1411
|
-
def transpose(
|
1412
|
-
iterable: Iterable[tuple[
|
1413
|
-
) -> tuple[list[
|
1397
|
+
def transpose[T1, T2, T3](
|
1398
|
+
iterable: Iterable[tuple[T1, T2, T3]], /
|
1399
|
+
) -> tuple[list[T1], list[T2], list[T3]]: ...
|
1414
1400
|
@overload
|
1415
|
-
def transpose(
|
1416
|
-
iterable: Iterable[tuple[
|
1417
|
-
) -> tuple[list[
|
1401
|
+
def transpose[T1, T2, T3, T4](
|
1402
|
+
iterable: Iterable[tuple[T1, T2, T3, T4]], /
|
1403
|
+
) -> tuple[list[T1], list[T2], list[T3], list[T4]]: ...
|
1418
1404
|
@overload
|
1419
|
-
def transpose(
|
1420
|
-
iterable: Iterable[tuple[
|
1421
|
-
) -> tuple[list[
|
1405
|
+
def transpose[T1, T2, T3, T4, T5](
|
1406
|
+
iterable: Iterable[tuple[T1, T2, T3, T4, T5]], /
|
1407
|
+
) -> tuple[list[T1], list[T2], list[T3], list[T4], list[T5]]: ...
|
1422
1408
|
def transpose(iterable: Iterable[tuple[Any]]) -> tuple[list[Any], ...]: # pyright: ignore[reportInconsistentOverload]
|
1423
1409
|
"""Typed verison of `transpose`."""
|
1424
1410
|
return tuple(map(list, zip(*iterable, strict=True)))
|
@@ -1427,9 +1413,9 @@ def transpose(iterable: Iterable[tuple[Any]]) -> tuple[list[Any], ...]: # pyrig
|
|
1427
1413
|
##
|
1428
1414
|
|
1429
1415
|
|
1430
|
-
def unique_everseen(
|
1431
|
-
iterable: Iterable[
|
1432
|
-
) -> Iterator[
|
1416
|
+
def unique_everseen[T](
|
1417
|
+
iterable: Iterable[T], /, *, key: Callable[[T], Any] | None = None
|
1418
|
+
) -> Iterator[T]:
|
1433
1419
|
"""Yield unique elements, preserving order."""
|
1434
1420
|
seenset = set()
|
1435
1421
|
seenset_add = seenset.add
|