dycw-utilities 0.129.10__py3-none-any.whl → 0.175.17__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.175.17.dist-info/METADATA +34 -0
- dycw_utilities-0.175.17.dist-info/RECORD +103 -0
- dycw_utilities-0.175.17.dist-info/WHEEL +4 -0
- dycw_utilities-0.175.17.dist-info/entry_points.txt +4 -0
- utilities/__init__.py +1 -1
- utilities/altair.py +14 -14
- utilities/asyncio.py +350 -819
- utilities/atomicwrites.py +18 -6
- utilities/atools.py +77 -22
- utilities/cachetools.py +24 -29
- utilities/click.py +393 -237
- utilities/concurrent.py +8 -11
- utilities/contextlib.py +216 -17
- utilities/contextvars.py +20 -1
- utilities/cryptography.py +3 -3
- utilities/dataclasses.py +83 -118
- utilities/docker.py +293 -0
- utilities/enum.py +26 -23
- utilities/errors.py +17 -3
- utilities/fastapi.py +29 -65
- utilities/fpdf2.py +3 -3
- utilities/functions.py +169 -416
- utilities/functools.py +18 -19
- utilities/git.py +9 -30
- utilities/grp.py +28 -0
- utilities/gzip.py +31 -0
- utilities/http.py +3 -2
- utilities/hypothesis.py +738 -589
- utilities/importlib.py +17 -1
- utilities/inflect.py +25 -0
- utilities/iterables.py +194 -262
- utilities/jinja2.py +148 -0
- utilities/json.py +70 -0
- utilities/libcst.py +38 -17
- utilities/lightweight_charts.py +5 -9
- utilities/logging.py +345 -543
- utilities/math.py +18 -13
- utilities/memory_profiler.py +11 -15
- utilities/more_itertools.py +200 -131
- utilities/operator.py +33 -29
- utilities/optuna.py +6 -6
- utilities/orjson.py +272 -137
- utilities/os.py +61 -4
- utilities/parse.py +59 -61
- utilities/pathlib.py +281 -40
- utilities/permissions.py +298 -0
- utilities/pickle.py +2 -2
- utilities/platform.py +24 -5
- utilities/polars.py +1214 -430
- utilities/polars_ols.py +1 -1
- utilities/postgres.py +408 -0
- utilities/pottery.py +113 -26
- utilities/pqdm.py +10 -11
- utilities/psutil.py +6 -57
- utilities/pwd.py +28 -0
- utilities/pydantic.py +4 -54
- utilities/pydantic_settings.py +240 -0
- utilities/pydantic_settings_sops.py +76 -0
- utilities/pyinstrument.py +8 -10
- utilities/pytest.py +227 -121
- utilities/pytest_plugins/__init__.py +1 -0
- utilities/pytest_plugins/pytest_randomly.py +23 -0
- utilities/pytest_plugins/pytest_regressions.py +56 -0
- utilities/pytest_regressions.py +26 -46
- utilities/random.py +13 -9
- utilities/re.py +58 -28
- utilities/redis.py +401 -550
- utilities/scipy.py +1 -1
- utilities/sentinel.py +10 -0
- utilities/shelve.py +4 -1
- utilities/shutil.py +25 -0
- utilities/slack_sdk.py +36 -106
- utilities/sqlalchemy.py +502 -473
- utilities/sqlalchemy_polars.py +38 -94
- utilities/string.py +2 -3
- utilities/subprocess.py +1572 -0
- utilities/tempfile.py +86 -4
- utilities/testbook.py +50 -0
- utilities/text.py +165 -42
- utilities/timer.py +37 -65
- utilities/traceback.py +158 -929
- utilities/types.py +146 -116
- utilities/typing.py +531 -71
- utilities/tzdata.py +1 -53
- utilities/tzlocal.py +6 -23
- utilities/uuid.py +43 -5
- utilities/version.py +27 -26
- utilities/whenever.py +1776 -386
- utilities/zoneinfo.py +84 -22
- dycw_utilities-0.129.10.dist-info/METADATA +0 -241
- dycw_utilities-0.129.10.dist-info/RECORD +0 -96
- dycw_utilities-0.129.10.dist-info/WHEEL +0 -4
- dycw_utilities-0.129.10.dist-info/licenses/LICENSE +0 -21
- utilities/datetime.py +0 -1409
- utilities/eventkit.py +0 -402
- utilities/loguru.py +0 -144
- utilities/luigi.py +0 -228
- utilities/period.py +0 -324
- utilities/pyrsistent.py +0 -89
- utilities/python_dotenv.py +0 -105
- utilities/streamlit.py +0 -105
- utilities/sys.py +0 -87
- utilities/tenacity.py +0 -145
utilities/iterables.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import builtins
|
|
4
|
-
import datetime as dt
|
|
5
4
|
from collections import Counter
|
|
6
5
|
from collections.abc import (
|
|
7
6
|
Callable,
|
|
@@ -19,14 +18,12 @@ from enum import Enum
|
|
|
19
18
|
from functools import cmp_to_key, partial, reduce
|
|
20
19
|
from itertools import accumulate, chain, groupby, islice, pairwise, product
|
|
21
20
|
from math import isnan
|
|
22
|
-
from operator import add,
|
|
21
|
+
from operator import add, or_
|
|
23
22
|
from typing import (
|
|
24
23
|
TYPE_CHECKING,
|
|
25
24
|
Any,
|
|
26
|
-
Generic,
|
|
27
25
|
Literal,
|
|
28
26
|
TypeGuard,
|
|
29
|
-
TypeVar,
|
|
30
27
|
assert_never,
|
|
31
28
|
cast,
|
|
32
29
|
overload,
|
|
@@ -34,7 +31,6 @@ from typing import (
|
|
|
34
31
|
)
|
|
35
32
|
|
|
36
33
|
from utilities.errors import ImpossibleCaseError
|
|
37
|
-
from utilities.functions import ensure_hashable, ensure_not_none, ensure_str
|
|
38
34
|
from utilities.math import (
|
|
39
35
|
_CheckIntegerEqualError,
|
|
40
36
|
_CheckIntegerEqualOrApproxError,
|
|
@@ -43,58 +39,35 @@ from utilities.math import (
|
|
|
43
39
|
check_integer,
|
|
44
40
|
)
|
|
45
41
|
from utilities.reprlib import get_repr
|
|
46
|
-
from utilities.sentinel import Sentinel, sentinel
|
|
47
|
-
from utilities.types import
|
|
48
|
-
from utilities.zoneinfo import UTC
|
|
42
|
+
from utilities.sentinel import Sentinel, is_sentinel, sentinel
|
|
43
|
+
from utilities.types import SupportsAdd, SupportsLT
|
|
49
44
|
|
|
50
45
|
if TYPE_CHECKING:
|
|
51
46
|
from types import NoneType
|
|
52
47
|
|
|
53
|
-
from utilities.types import MaybeIterable,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
_K = TypeVar("_K")
|
|
57
|
-
_T = TypeVar("_T")
|
|
58
|
-
_U = TypeVar("_U")
|
|
59
|
-
_V = TypeVar("_V")
|
|
60
|
-
_W = TypeVar("_W")
|
|
61
|
-
_T1 = TypeVar("_T1")
|
|
62
|
-
_T2 = TypeVar("_T2")
|
|
63
|
-
_T3 = TypeVar("_T3")
|
|
64
|
-
_T4 = TypeVar("_T4")
|
|
65
|
-
_T5 = TypeVar("_T5")
|
|
48
|
+
from utilities.types import MaybeIterable, Sign, StrMapping
|
|
66
49
|
|
|
67
50
|
|
|
68
51
|
##
|
|
69
52
|
|
|
70
53
|
|
|
71
|
-
def always_iterable(obj: MaybeIterable[
|
|
54
|
+
def always_iterable[T](obj: MaybeIterable[T], /) -> Iterable[T]:
|
|
72
55
|
"""Typed version of `always_iterable`."""
|
|
73
56
|
obj = cast("Any", obj)
|
|
74
57
|
if isinstance(obj, str | bytes):
|
|
75
|
-
return cast("list[
|
|
58
|
+
return cast("list[T]", [obj])
|
|
76
59
|
try:
|
|
77
|
-
return iter(cast("Iterable[
|
|
60
|
+
return iter(cast("Iterable[T]", obj))
|
|
78
61
|
except TypeError:
|
|
79
|
-
return cast("list[
|
|
62
|
+
return cast("list[T]", [obj])
|
|
80
63
|
|
|
81
64
|
|
|
82
65
|
##
|
|
83
66
|
|
|
84
67
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
) ->
|
|
88
|
-
"""Ensure an object is always hashable."""
|
|
89
|
-
return None if obj is None else tuple(always_iterable(obj))
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
##
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def apply_bijection(
|
|
96
|
-
func: Callable[[_T], _U], iterable: Iterable[_T], /
|
|
97
|
-
) -> Mapping[_T, _U]:
|
|
68
|
+
def apply_bijection[T, U](
|
|
69
|
+
func: Callable[[T], U], iterable: Iterable[T], /
|
|
70
|
+
) -> Mapping[T, U]:
|
|
98
71
|
"""Apply a function bijectively."""
|
|
99
72
|
keys = list(iterable)
|
|
100
73
|
try:
|
|
@@ -114,21 +87,21 @@ def apply_bijection(
|
|
|
114
87
|
|
|
115
88
|
|
|
116
89
|
@dataclass(kw_only=True, slots=True)
|
|
117
|
-
class ApplyBijectionError(Exception
|
|
118
|
-
keys: list[
|
|
119
|
-
counts: Mapping[
|
|
90
|
+
class ApplyBijectionError[T](Exception):
|
|
91
|
+
keys: list[T]
|
|
92
|
+
counts: Mapping[T, int]
|
|
120
93
|
|
|
121
94
|
|
|
122
95
|
@dataclass(kw_only=True, slots=True)
|
|
123
|
-
class _ApplyBijectionDuplicateKeysError(ApplyBijectionError[
|
|
96
|
+
class _ApplyBijectionDuplicateKeysError[T](ApplyBijectionError[T]):
|
|
124
97
|
@override
|
|
125
98
|
def __str__(self) -> str:
|
|
126
99
|
return f"Keys {get_repr(self.keys)} must not contain duplicates; got {get_repr(self.counts)}"
|
|
127
100
|
|
|
128
101
|
|
|
129
102
|
@dataclass(kw_only=True, slots=True)
|
|
130
|
-
class _ApplyBijectionDuplicateValuesError
|
|
131
|
-
values: list[
|
|
103
|
+
class _ApplyBijectionDuplicateValuesError[T, U](ApplyBijectionError[T]):
|
|
104
|
+
values: list[U]
|
|
132
105
|
|
|
133
106
|
@override
|
|
134
107
|
def __str__(self) -> str:
|
|
@@ -138,7 +111,7 @@ class _ApplyBijectionDuplicateValuesError(ApplyBijectionError[_T], Generic[_T, _
|
|
|
138
111
|
##
|
|
139
112
|
|
|
140
113
|
|
|
141
|
-
def apply_to_tuple(func: Callable[...,
|
|
114
|
+
def apply_to_tuple[T](func: Callable[..., T], args: tuple[Any, ...], /) -> T:
|
|
142
115
|
"""Apply a function to a tuple of args."""
|
|
143
116
|
return apply_to_varargs(func, *args)
|
|
144
117
|
|
|
@@ -146,7 +119,7 @@ def apply_to_tuple(func: Callable[..., _T], args: tuple[Any, ...], /) -> _T:
|
|
|
146
119
|
##
|
|
147
120
|
|
|
148
121
|
|
|
149
|
-
def apply_to_varargs(func: Callable[...,
|
|
122
|
+
def apply_to_varargs[T](func: Callable[..., T], *args: Any) -> T:
|
|
150
123
|
"""Apply a function to a variable number of arguments."""
|
|
151
124
|
return func(*args)
|
|
152
125
|
|
|
@@ -155,17 +128,17 @@ def apply_to_varargs(func: Callable[..., _T], *args: Any) -> _T:
|
|
|
155
128
|
|
|
156
129
|
|
|
157
130
|
@overload
|
|
158
|
-
def chain_mappings(
|
|
159
|
-
*mappings: Mapping[
|
|
160
|
-
) -> Mapping[
|
|
131
|
+
def chain_mappings[K, V](
|
|
132
|
+
*mappings: Mapping[K, V], list: Literal[True]
|
|
133
|
+
) -> Mapping[K, Sequence[V]]: ...
|
|
161
134
|
@overload
|
|
162
|
-
def chain_mappings(
|
|
163
|
-
*mappings: Mapping[
|
|
164
|
-
) -> Mapping[
|
|
165
|
-
def chain_mappings(
|
|
166
|
-
*mappings: Mapping[
|
|
135
|
+
def chain_mappings[K, V](
|
|
136
|
+
*mappings: Mapping[K, V], list: bool = False
|
|
137
|
+
) -> Mapping[K, Iterable[V]]: ...
|
|
138
|
+
def chain_mappings[K, V](
|
|
139
|
+
*mappings: Mapping[K, V],
|
|
167
140
|
list: bool = False, # noqa: A002
|
|
168
|
-
) -> Mapping[
|
|
141
|
+
) -> Mapping[K, Iterable[V]]:
|
|
169
142
|
"""Chain the values of a set of mappings."""
|
|
170
143
|
try:
|
|
171
144
|
first, *rest = mappings
|
|
@@ -178,9 +151,9 @@ def chain_mappings(
|
|
|
178
151
|
return reduced
|
|
179
152
|
|
|
180
153
|
|
|
181
|
-
def _chain_mappings_one(
|
|
182
|
-
acc: Mapping[
|
|
183
|
-
) -> Mapping[
|
|
154
|
+
def _chain_mappings_one[K, V](
|
|
155
|
+
acc: Mapping[K, Iterable[V]], el: Mapping[K, V], /
|
|
156
|
+
) -> Mapping[K, Iterable[V]]:
|
|
184
157
|
"""Chain the values of a set of mappings."""
|
|
185
158
|
out = dict(acc)
|
|
186
159
|
for key, value in el.items():
|
|
@@ -191,7 +164,7 @@ def _chain_mappings_one(
|
|
|
191
164
|
##
|
|
192
165
|
|
|
193
166
|
|
|
194
|
-
def chain_maybe_iterables(*maybe_iterables: MaybeIterable[
|
|
167
|
+
def chain_maybe_iterables[T](*maybe_iterables: MaybeIterable[T]) -> Iterable[T]:
|
|
195
168
|
"""Chain a set of maybe iterables."""
|
|
196
169
|
iterables = map(always_iterable, maybe_iterables)
|
|
197
170
|
return chain.from_iterable(iterables)
|
|
@@ -200,7 +173,7 @@ def chain_maybe_iterables(*maybe_iterables: MaybeIterable[_T]) -> Iterable[_T]:
|
|
|
200
173
|
##
|
|
201
174
|
|
|
202
175
|
|
|
203
|
-
def chain_nullable(*maybe_iterables: Iterable[
|
|
176
|
+
def chain_nullable[T](*maybe_iterables: Iterable[T | None] | None) -> Iterable[T]:
|
|
204
177
|
"""Chain a set of values; ignoring nulls."""
|
|
205
178
|
iterables = (mi for mi in maybe_iterables if mi is not None)
|
|
206
179
|
values = ((i for i in it if i is not None) for it in iterables)
|
|
@@ -219,7 +192,7 @@ def check_bijection(mapping: Mapping[Any, Hashable], /) -> None:
|
|
|
219
192
|
|
|
220
193
|
|
|
221
194
|
@dataclass(kw_only=True, slots=True)
|
|
222
|
-
class CheckBijectionError
|
|
195
|
+
class CheckBijectionError[THashable](Exception):
|
|
223
196
|
mapping: Mapping[Any, THashable]
|
|
224
197
|
counts: Mapping[THashable, int]
|
|
225
198
|
|
|
@@ -239,7 +212,7 @@ def check_duplicates(iterable: Iterable[Hashable], /) -> None:
|
|
|
239
212
|
|
|
240
213
|
|
|
241
214
|
@dataclass(kw_only=True, slots=True)
|
|
242
|
-
class CheckDuplicatesError
|
|
215
|
+
class CheckDuplicatesError[THashable](Exception):
|
|
243
216
|
iterable: Iterable[THashable]
|
|
244
217
|
counts: Mapping[THashable, int]
|
|
245
218
|
|
|
@@ -262,8 +235,7 @@ def check_iterables_equal(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
|
262
235
|
if lv != rv:
|
|
263
236
|
errors.append((i, lv, rv))
|
|
264
237
|
except ValueError as error:
|
|
265
|
-
|
|
266
|
-
match msg:
|
|
238
|
+
match one(error.args):
|
|
267
239
|
case "zip() argument 2 is longer than argument 1":
|
|
268
240
|
state = "right_longer"
|
|
269
241
|
case "zip() argument 2 is shorter than argument 1":
|
|
@@ -282,10 +254,10 @@ type _CheckIterablesEqualState = Literal["left_longer", "right_longer"]
|
|
|
282
254
|
|
|
283
255
|
|
|
284
256
|
@dataclass(kw_only=True, slots=True)
|
|
285
|
-
class CheckIterablesEqualError(Exception
|
|
286
|
-
left: list[
|
|
287
|
-
right: list[
|
|
288
|
-
errors: list[tuple[int,
|
|
257
|
+
class CheckIterablesEqualError[T](Exception):
|
|
258
|
+
left: list[T]
|
|
259
|
+
right: list[T]
|
|
260
|
+
errors: list[tuple[int, T, T]]
|
|
289
261
|
state: _CheckIterablesEqualState | None
|
|
290
262
|
|
|
291
263
|
@override
|
|
@@ -311,7 +283,7 @@ class CheckIterablesEqualError(Exception, Generic[_T]):
|
|
|
311
283
|
yield "right was longer"
|
|
312
284
|
case None:
|
|
313
285
|
pass
|
|
314
|
-
case
|
|
286
|
+
case never:
|
|
315
287
|
assert_never(never)
|
|
316
288
|
|
|
317
289
|
|
|
@@ -436,12 +408,12 @@ def check_mappings_equal(left: Mapping[Any, Any], right: Mapping[Any, Any], /) -
|
|
|
436
408
|
|
|
437
409
|
|
|
438
410
|
@dataclass(kw_only=True, slots=True)
|
|
439
|
-
class CheckMappingsEqualError
|
|
440
|
-
left: Mapping[
|
|
441
|
-
right: Mapping[
|
|
442
|
-
left_extra: AbstractSet[
|
|
443
|
-
right_extra: AbstractSet[
|
|
444
|
-
errors: list[tuple[
|
|
411
|
+
class CheckMappingsEqualError[K, V](Exception):
|
|
412
|
+
left: Mapping[K, V]
|
|
413
|
+
right: Mapping[K, V]
|
|
414
|
+
left_extra: AbstractSet[K]
|
|
415
|
+
right_extra: AbstractSet[K]
|
|
416
|
+
errors: list[tuple[K, V, V]]
|
|
445
417
|
|
|
446
418
|
@override
|
|
447
419
|
def __str__(self) -> str:
|
|
@@ -486,11 +458,11 @@ def check_sets_equal(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
|
486
458
|
|
|
487
459
|
|
|
488
460
|
@dataclass(kw_only=True, slots=True)
|
|
489
|
-
class CheckSetsEqualError(Exception
|
|
490
|
-
left: AbstractSet[
|
|
491
|
-
right: AbstractSet[
|
|
492
|
-
left_extra: AbstractSet[
|
|
493
|
-
right_extra: AbstractSet[
|
|
461
|
+
class CheckSetsEqualError[T](Exception):
|
|
462
|
+
left: AbstractSet[T]
|
|
463
|
+
right: AbstractSet[T]
|
|
464
|
+
left_extra: AbstractSet[T]
|
|
465
|
+
right_extra: AbstractSet[T]
|
|
494
466
|
|
|
495
467
|
@override
|
|
496
468
|
def __str__(self) -> str:
|
|
@@ -533,11 +505,11 @@ def check_submapping(left: Mapping[Any, Any], right: Mapping[Any, Any], /) -> No
|
|
|
533
505
|
|
|
534
506
|
|
|
535
507
|
@dataclass(kw_only=True, slots=True)
|
|
536
|
-
class CheckSubMappingError
|
|
537
|
-
left: Mapping[
|
|
538
|
-
right: Mapping[
|
|
539
|
-
extra: AbstractSet[
|
|
540
|
-
errors: list[tuple[
|
|
508
|
+
class CheckSubMappingError[K, V](Exception):
|
|
509
|
+
left: Mapping[K, V]
|
|
510
|
+
right: Mapping[K, V]
|
|
511
|
+
extra: AbstractSet[K]
|
|
512
|
+
errors: list[tuple[K, V, V]]
|
|
541
513
|
|
|
542
514
|
@override
|
|
543
515
|
def __str__(self) -> str:
|
|
@@ -572,10 +544,10 @@ def check_subset(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
|
572
544
|
|
|
573
545
|
|
|
574
546
|
@dataclass(kw_only=True, slots=True)
|
|
575
|
-
class CheckSubSetError(Exception
|
|
576
|
-
left: AbstractSet[
|
|
577
|
-
right: AbstractSet[
|
|
578
|
-
extra: AbstractSet[
|
|
547
|
+
class CheckSubSetError[T](Exception):
|
|
548
|
+
left: AbstractSet[T]
|
|
549
|
+
right: AbstractSet[T]
|
|
550
|
+
extra: AbstractSet[T]
|
|
579
551
|
|
|
580
552
|
@override
|
|
581
553
|
def __str__(self) -> str:
|
|
@@ -604,11 +576,11 @@ def check_supermapping(left: Mapping[Any, Any], right: Mapping[Any, Any], /) ->
|
|
|
604
576
|
|
|
605
577
|
|
|
606
578
|
@dataclass(kw_only=True, slots=True)
|
|
607
|
-
class CheckSuperMappingError
|
|
608
|
-
left: Mapping[
|
|
609
|
-
right: Mapping[
|
|
610
|
-
extra: AbstractSet[
|
|
611
|
-
errors: list[tuple[
|
|
579
|
+
class CheckSuperMappingError[K, V](Exception):
|
|
580
|
+
left: Mapping[K, V]
|
|
581
|
+
right: Mapping[K, V]
|
|
582
|
+
extra: AbstractSet[K]
|
|
583
|
+
errors: list[tuple[K, V, V]]
|
|
612
584
|
|
|
613
585
|
@override
|
|
614
586
|
def __str__(self) -> str:
|
|
@@ -643,10 +615,10 @@ def check_superset(left: Iterable[Any], right: Iterable[Any], /) -> None:
|
|
|
643
615
|
|
|
644
616
|
|
|
645
617
|
@dataclass(kw_only=True, slots=True)
|
|
646
|
-
class CheckSuperSetError(Exception
|
|
647
|
-
left: AbstractSet[
|
|
648
|
-
right: AbstractSet[
|
|
649
|
-
extra: AbstractSet[
|
|
618
|
+
class CheckSuperSetError[T](Exception):
|
|
619
|
+
left: AbstractSet[T]
|
|
620
|
+
right: AbstractSet[T]
|
|
621
|
+
extra: AbstractSet[T]
|
|
650
622
|
|
|
651
623
|
@override
|
|
652
624
|
def __str__(self) -> str:
|
|
@@ -695,7 +667,7 @@ class _CheckUniqueModuloCaseDuplicateLowerCaseStringsError(CheckUniqueModuloCase
|
|
|
695
667
|
##
|
|
696
668
|
|
|
697
669
|
|
|
698
|
-
def cmp_nullable(x:
|
|
670
|
+
def cmp_nullable[T: SupportsLT](x: T | None, y: T | None, /) -> Sign:
|
|
699
671
|
"""Compare two nullable objects."""
|
|
700
672
|
match x, y:
|
|
701
673
|
case None, None:
|
|
@@ -706,14 +678,14 @@ def cmp_nullable(x: TSupportsLT | None, y: TSupportsLT | None, /) -> Sign:
|
|
|
706
678
|
return 1
|
|
707
679
|
case _, _:
|
|
708
680
|
return cast("Sign", (x > y) - (x < y))
|
|
709
|
-
case
|
|
681
|
+
case never:
|
|
710
682
|
assert_never(never)
|
|
711
683
|
|
|
712
684
|
|
|
713
685
|
##
|
|
714
686
|
|
|
715
687
|
|
|
716
|
-
def chunked(iterable: Iterable[
|
|
688
|
+
def chunked[T](iterable: Iterable[T], n: int, /) -> Iterator[Sequence[T]]:
|
|
717
689
|
"""Break an iterable into lists of length n."""
|
|
718
690
|
return iter(partial(take, n, iter(iterable)), [])
|
|
719
691
|
|
|
@@ -721,18 +693,6 @@ def chunked(iterable: Iterable[_T], n: int, /) -> Iterator[Sequence[_T]]:
|
|
|
721
693
|
##
|
|
722
694
|
|
|
723
695
|
|
|
724
|
-
def ensure_hashables(
|
|
725
|
-
*args: Any, **kwargs: Any
|
|
726
|
-
) -> tuple[list[Hashable], dict[str, Hashable]]:
|
|
727
|
-
"""Ensure a set of positional & keyword arguments are all hashable."""
|
|
728
|
-
hash_args = list(map(ensure_hashable, args))
|
|
729
|
-
hash_kwargs = {k: ensure_hashable(v) for k, v in kwargs.items()}
|
|
730
|
-
return hash_args, hash_kwargs
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
##
|
|
734
|
-
|
|
735
|
-
|
|
736
696
|
def ensure_iterable(obj: Any, /) -> Iterable[Any]:
|
|
737
697
|
"""Ensure an object is iterable."""
|
|
738
698
|
if is_iterable(obj):
|
|
@@ -771,10 +731,30 @@ class EnsureIterableNotStrError(Exception):
|
|
|
771
731
|
##
|
|
772
732
|
|
|
773
733
|
|
|
774
|
-
|
|
734
|
+
_EDGE: int = 5
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
def enumerate_with_edge[T](
|
|
738
|
+
iterable: Iterable[T], /, *, start: int = 0, edge: int = _EDGE
|
|
739
|
+
) -> Iterator[tuple[int, int, bool, T]]:
|
|
740
|
+
"""Enumerate an iterable, with the edge items marked."""
|
|
741
|
+
as_list = list(iterable)
|
|
742
|
+
total = len(as_list)
|
|
743
|
+
indices = set(range(edge)) | set(range(total)[-edge:])
|
|
744
|
+
is_edge = (i in indices for i in range(total))
|
|
745
|
+
for (i, value), is_edge_i in zip(
|
|
746
|
+
enumerate(as_list, start=start), is_edge, strict=True
|
|
747
|
+
):
|
|
748
|
+
yield i, total, is_edge_i, value
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
##
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def expanding_window[T](iterable: Iterable[T], /) -> islice[list[T]]:
|
|
775
755
|
"""Yield an expanding window over an iterable."""
|
|
776
756
|
|
|
777
|
-
def func(acc: Iterable[
|
|
757
|
+
def func(acc: Iterable[T], el: T, /) -> list[T]:
|
|
778
758
|
return list(chain(acc, [el]))
|
|
779
759
|
|
|
780
760
|
return islice(accumulate(iterable, func=func, initial=[]), 1, None)
|
|
@@ -784,31 +764,31 @@ def expanding_window(iterable: Iterable[_T], /) -> islice[list[_T]]:
|
|
|
784
764
|
|
|
785
765
|
|
|
786
766
|
@overload
|
|
787
|
-
def filter_include_and_exclude(
|
|
788
|
-
iterable: Iterable[
|
|
767
|
+
def filter_include_and_exclude[T, U](
|
|
768
|
+
iterable: Iterable[T],
|
|
789
769
|
/,
|
|
790
770
|
*,
|
|
791
|
-
include: MaybeIterable[
|
|
792
|
-
exclude: MaybeIterable[
|
|
793
|
-
key: Callable[[
|
|
794
|
-
) -> Iterable[
|
|
771
|
+
include: MaybeIterable[U] | None = None,
|
|
772
|
+
exclude: MaybeIterable[U] | None = None,
|
|
773
|
+
key: Callable[[T], U],
|
|
774
|
+
) -> Iterable[T]: ...
|
|
795
775
|
@overload
|
|
796
|
-
def filter_include_and_exclude(
|
|
797
|
-
iterable: Iterable[
|
|
776
|
+
def filter_include_and_exclude[T](
|
|
777
|
+
iterable: Iterable[T],
|
|
798
778
|
/,
|
|
799
779
|
*,
|
|
800
|
-
include: MaybeIterable[
|
|
801
|
-
exclude: MaybeIterable[
|
|
802
|
-
key: Callable[[
|
|
803
|
-
) -> Iterable[
|
|
804
|
-
def filter_include_and_exclude(
|
|
805
|
-
iterable: Iterable[
|
|
780
|
+
include: MaybeIterable[T] | None = None,
|
|
781
|
+
exclude: MaybeIterable[T] | None = None,
|
|
782
|
+
key: Callable[[T], Any] | None = None,
|
|
783
|
+
) -> Iterable[T]: ...
|
|
784
|
+
def filter_include_and_exclude[T, U](
|
|
785
|
+
iterable: Iterable[T],
|
|
806
786
|
/,
|
|
807
787
|
*,
|
|
808
|
-
include: MaybeIterable[
|
|
809
|
-
exclude: MaybeIterable[
|
|
810
|
-
key: Callable[[
|
|
811
|
-
) -> Iterable[
|
|
788
|
+
include: MaybeIterable[U] | None = None,
|
|
789
|
+
exclude: MaybeIterable[U] | None = None,
|
|
790
|
+
key: Callable[[T], U] | None = None,
|
|
791
|
+
) -> Iterable[T]:
|
|
812
792
|
"""Filter an iterable based on an inclusion/exclusion pair."""
|
|
813
793
|
include, exclude = resolve_include_and_exclude(include=include, exclude=exclude)
|
|
814
794
|
if include is not None:
|
|
@@ -827,35 +807,17 @@ def filter_include_and_exclude(
|
|
|
827
807
|
##
|
|
828
808
|
|
|
829
809
|
|
|
830
|
-
def group_consecutive_integers(iterable: Iterable[int], /) -> Iterable[tuple[int, int]]:
|
|
831
|
-
"""Group consecutive integers."""
|
|
832
|
-
integers = sorted(iterable)
|
|
833
|
-
for _, group in groupby(enumerate(integers), key=lambda x: x[1] - x[0]):
|
|
834
|
-
as_list = list(map(itemgetter(1), group))
|
|
835
|
-
yield as_list[0], as_list[-1]
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
def ungroup_consecutive_integers(
|
|
839
|
-
iterable: Iterable[tuple[int, int]], /
|
|
840
|
-
) -> Iterable[int]:
|
|
841
|
-
"""Ungroup consecutive integers."""
|
|
842
|
-
return chain.from_iterable(range(start, end + 1) for start, end in iterable)
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
##
|
|
846
|
-
|
|
847
|
-
|
|
848
810
|
@overload
|
|
849
|
-
def groupby_lists(
|
|
850
|
-
iterable: Iterable[
|
|
851
|
-
) -> Iterator[tuple[
|
|
811
|
+
def groupby_lists[T](
|
|
812
|
+
iterable: Iterable[T], /, *, key: None = None
|
|
813
|
+
) -> Iterator[tuple[T, list[T]]]: ...
|
|
852
814
|
@overload
|
|
853
|
-
def groupby_lists(
|
|
854
|
-
iterable: Iterable[
|
|
855
|
-
) -> Iterator[tuple[
|
|
856
|
-
def groupby_lists(
|
|
857
|
-
iterable: Iterable[
|
|
858
|
-
) -> Iterator[tuple[
|
|
815
|
+
def groupby_lists[T, U](
|
|
816
|
+
iterable: Iterable[T], /, *, key: Callable[[T], U]
|
|
817
|
+
) -> Iterator[tuple[U, list[T]]]: ...
|
|
818
|
+
def groupby_lists[T, U](
|
|
819
|
+
iterable: Iterable[T], /, *, key: Callable[[T], U] | None = None
|
|
820
|
+
) -> Iterator[tuple[T, list[T]]] | Iterator[tuple[U, list[T]]]:
|
|
859
821
|
"""Yield consecutive keys and groups (as lists)."""
|
|
860
822
|
if key is None:
|
|
861
823
|
for k, group in groupby(iterable):
|
|
@@ -868,7 +830,7 @@ def groupby_lists(
|
|
|
868
830
|
##
|
|
869
831
|
|
|
870
832
|
|
|
871
|
-
def hashable_to_iterable(obj:
|
|
833
|
+
def hashable_to_iterable[T: Hashable](obj: T | None, /) -> tuple[T, ...] | None:
|
|
872
834
|
"""Lift a hashable singleton to an iterable of hashables."""
|
|
873
835
|
return None if obj is None else (obj,)
|
|
874
836
|
|
|
@@ -904,9 +866,9 @@ def is_iterable_not_str(obj: Any, /) -> TypeGuard[Iterable[Any]]:
|
|
|
904
866
|
##
|
|
905
867
|
|
|
906
868
|
|
|
907
|
-
def map_mapping(
|
|
908
|
-
func: Callable[[
|
|
909
|
-
) -> Mapping[
|
|
869
|
+
def map_mapping[K, V, W](
|
|
870
|
+
func: Callable[[V], W], mapping: Mapping[K, V], /
|
|
871
|
+
) -> Mapping[K, W]:
|
|
910
872
|
"""Map a function over the values of a mapping."""
|
|
911
873
|
return {k: func(v) for k, v in mapping.items()}
|
|
912
874
|
|
|
@@ -914,7 +876,7 @@ def map_mapping(
|
|
|
914
876
|
##
|
|
915
877
|
|
|
916
878
|
|
|
917
|
-
def merge_mappings(*mappings: Mapping[
|
|
879
|
+
def merge_mappings[K, V](*mappings: Mapping[K, V]) -> Mapping[K, V]:
|
|
918
880
|
"""Merge a set of mappings."""
|
|
919
881
|
return reduce(or_, map(dict, mappings), {})
|
|
920
882
|
|
|
@@ -922,7 +884,7 @@ def merge_mappings(*mappings: Mapping[_K, _V]) -> Mapping[_K, _V]:
|
|
|
922
884
|
##
|
|
923
885
|
|
|
924
886
|
|
|
925
|
-
def merge_sets(*iterables: Iterable[
|
|
887
|
+
def merge_sets[T](*iterables: Iterable[T]) -> AbstractSet[T]:
|
|
926
888
|
"""Merge a set of sets."""
|
|
927
889
|
return reduce(or_, map(set, iterables), set())
|
|
928
890
|
|
|
@@ -969,9 +931,9 @@ class MergeStrMappingsError(Exception):
|
|
|
969
931
|
##
|
|
970
932
|
|
|
971
933
|
|
|
972
|
-
def one(*iterables: Iterable[
|
|
934
|
+
def one[T](*iterables: Iterable[T]) -> T:
|
|
973
935
|
"""Return the unique value in a set of iterables."""
|
|
974
|
-
it =
|
|
936
|
+
it = chain(*iterables)
|
|
975
937
|
try:
|
|
976
938
|
first = next(it)
|
|
977
939
|
except StopIteration:
|
|
@@ -984,21 +946,21 @@ def one(*iterables: Iterable[_T]) -> _T:
|
|
|
984
946
|
|
|
985
947
|
|
|
986
948
|
@dataclass(kw_only=True, slots=True)
|
|
987
|
-
class OneError(Exception
|
|
988
|
-
iterables: tuple[Iterable[
|
|
949
|
+
class OneError[T](Exception):
|
|
950
|
+
iterables: tuple[Iterable[T], ...]
|
|
989
951
|
|
|
990
952
|
|
|
991
953
|
@dataclass(kw_only=True, slots=True)
|
|
992
|
-
class OneEmptyError(OneError[
|
|
954
|
+
class OneEmptyError[T](OneError[T]):
|
|
993
955
|
@override
|
|
994
956
|
def __str__(self) -> str:
|
|
995
957
|
return f"Iterable(s) {get_repr(self.iterables)} must not be empty"
|
|
996
958
|
|
|
997
959
|
|
|
998
960
|
@dataclass(kw_only=True, slots=True)
|
|
999
|
-
class OneNonUniqueError(OneError
|
|
1000
|
-
first:
|
|
1001
|
-
second:
|
|
961
|
+
class OneNonUniqueError[T](OneError):
|
|
962
|
+
first: T
|
|
963
|
+
second: T
|
|
1002
964
|
|
|
1003
965
|
@override
|
|
1004
966
|
def __str__(self) -> str:
|
|
@@ -1008,7 +970,7 @@ class OneNonUniqueError(OneError, Generic[_T]):
|
|
|
1008
970
|
##
|
|
1009
971
|
|
|
1010
972
|
|
|
1011
|
-
def one_maybe(*objs: MaybeIterable[
|
|
973
|
+
def one_maybe[T](*objs: MaybeIterable[T]) -> T:
|
|
1012
974
|
"""Return the unique value in a set of values/iterables."""
|
|
1013
975
|
try:
|
|
1014
976
|
return one(chain_maybe_iterables(*objs))
|
|
@@ -1032,10 +994,10 @@ class OneMaybeEmptyError(OneMaybeError):
|
|
|
1032
994
|
|
|
1033
995
|
|
|
1034
996
|
@dataclass(kw_only=True, slots=True)
|
|
1035
|
-
class OneMaybeNonUniqueError(OneMaybeError
|
|
1036
|
-
objs: tuple[MaybeIterable[
|
|
1037
|
-
first:
|
|
1038
|
-
second:
|
|
997
|
+
class OneMaybeNonUniqueError[T](OneMaybeError):
|
|
998
|
+
objs: tuple[MaybeIterable[T], ...]
|
|
999
|
+
first: T
|
|
1000
|
+
second: T
|
|
1039
1001
|
|
|
1040
1002
|
@override
|
|
1041
1003
|
def __str__(self) -> str:
|
|
@@ -1064,7 +1026,7 @@ def one_str(
|
|
|
1064
1026
|
it = (t for t in as_list if t.startswith(text))
|
|
1065
1027
|
case True, False:
|
|
1066
1028
|
it = (t for t in as_list if t.lower().startswith(text.lower()))
|
|
1067
|
-
case
|
|
1029
|
+
case never:
|
|
1068
1030
|
assert_never(never)
|
|
1069
1031
|
try:
|
|
1070
1032
|
return one(it)
|
|
@@ -1105,7 +1067,7 @@ class OneStrEmptyError(OneStrError):
|
|
|
1105
1067
|
tail = f"any string starting with {self.text!r}"
|
|
1106
1068
|
case True, False:
|
|
1107
1069
|
tail = f"any string starting with {self.text!r} (modulo case)"
|
|
1108
|
-
case
|
|
1070
|
+
case never:
|
|
1109
1071
|
assert_never(never)
|
|
1110
1072
|
return f"{head} {tail}"
|
|
1111
1073
|
|
|
@@ -1127,7 +1089,7 @@ class OneStrNonUniqueError(OneStrError):
|
|
|
1127
1089
|
mid = f"exactly one string starting with {self.text!r}"
|
|
1128
1090
|
case True, False:
|
|
1129
1091
|
mid = f"exactly one string starting with {self.text!r} (modulo case)"
|
|
1130
|
-
case
|
|
1092
|
+
case never:
|
|
1131
1093
|
assert_never(never)
|
|
1132
1094
|
return f"{head} {mid}; got {self.first!r}, {self.second!r} and perhaps more"
|
|
1133
1095
|
|
|
@@ -1135,7 +1097,7 @@ class OneStrNonUniqueError(OneStrError):
|
|
|
1135
1097
|
##
|
|
1136
1098
|
|
|
1137
1099
|
|
|
1138
|
-
def one_unique(*iterables: Iterable[
|
|
1100
|
+
def one_unique[T: Hashable](*iterables: Iterable[T]) -> T:
|
|
1139
1101
|
"""Return the set-unique value in a set of iterables."""
|
|
1140
1102
|
try:
|
|
1141
1103
|
return one(set(chain(*iterables)))
|
|
@@ -1159,7 +1121,7 @@ class OneUniqueEmptyError(OneUniqueError):
|
|
|
1159
1121
|
|
|
1160
1122
|
|
|
1161
1123
|
@dataclass(kw_only=True, slots=True)
|
|
1162
|
-
class OneUniqueNonUniqueError
|
|
1124
|
+
class OneUniqueNonUniqueError[THashable](OneUniqueError):
|
|
1163
1125
|
iterables: tuple[MaybeIterable[THashable], ...]
|
|
1164
1126
|
first: THashable
|
|
1165
1127
|
second: THashable
|
|
@@ -1172,7 +1134,7 @@ class OneUniqueNonUniqueError(OneUniqueError, Generic[THashable]):
|
|
|
1172
1134
|
##
|
|
1173
1135
|
|
|
1174
1136
|
|
|
1175
|
-
def pairwise_tail(iterable: Iterable[
|
|
1137
|
+
def pairwise_tail[T](iterable: Iterable[T], /) -> Iterator[tuple[T, T | Sentinel]]:
|
|
1176
1138
|
"""Return pairwise elements, with the last paired with the sentinel."""
|
|
1177
1139
|
return pairwise(chain(iterable, [sentinel]))
|
|
1178
1140
|
|
|
@@ -1180,11 +1142,11 @@ def pairwise_tail(iterable: Iterable[_T], /) -> Iterator[tuple[_T, _T | Sentinel
|
|
|
1180
1142
|
##
|
|
1181
1143
|
|
|
1182
1144
|
|
|
1183
|
-
def product_dicts(mapping: Mapping[
|
|
1145
|
+
def product_dicts[K, V](mapping: Mapping[K, Iterable[V]], /) -> Iterator[Mapping[K, V]]:
|
|
1184
1146
|
"""Return the cartesian product of the values in a mapping, as mappings."""
|
|
1185
1147
|
keys = list(mapping)
|
|
1186
1148
|
for values in product(*mapping.values()):
|
|
1187
|
-
yield cast("Mapping[
|
|
1149
|
+
yield cast("Mapping[K, V]", dict(zip(keys, values, strict=True)))
|
|
1188
1150
|
|
|
1189
1151
|
|
|
1190
1152
|
##
|
|
@@ -1241,41 +1203,39 @@ class _RangePartitionsNumError(RangePartitionsError):
|
|
|
1241
1203
|
|
|
1242
1204
|
|
|
1243
1205
|
@overload
|
|
1244
|
-
def reduce_mappings(
|
|
1245
|
-
func: Callable[[
|
|
1246
|
-
) -> Mapping[
|
|
1206
|
+
def reduce_mappings[K, V](
|
|
1207
|
+
func: Callable[[V, V], V], sequence: Iterable[Mapping[K, V]], /
|
|
1208
|
+
) -> Mapping[K, V]: ...
|
|
1247
1209
|
@overload
|
|
1248
|
-
def reduce_mappings(
|
|
1249
|
-
func: Callable[[
|
|
1250
|
-
sequence: Iterable[Mapping[
|
|
1210
|
+
def reduce_mappings[K, V, W](
|
|
1211
|
+
func: Callable[[W, V], W],
|
|
1212
|
+
sequence: Iterable[Mapping[K, V]],
|
|
1251
1213
|
/,
|
|
1252
1214
|
*,
|
|
1253
|
-
initial:
|
|
1254
|
-
) -> Mapping[
|
|
1255
|
-
def reduce_mappings(
|
|
1256
|
-
func: Callable[[
|
|
1257
|
-
sequence: Iterable[Mapping[
|
|
1215
|
+
initial: W | Sentinel = sentinel,
|
|
1216
|
+
) -> Mapping[K, W]: ...
|
|
1217
|
+
def reduce_mappings[K, V, W](
|
|
1218
|
+
func: Callable[[V, V], V] | Callable[[W, V], W],
|
|
1219
|
+
sequence: Iterable[Mapping[K, V]],
|
|
1258
1220
|
/,
|
|
1259
1221
|
*,
|
|
1260
|
-
initial:
|
|
1261
|
-
) -> Mapping[
|
|
1222
|
+
initial: W | Sentinel = sentinel,
|
|
1223
|
+
) -> Mapping[K, V | W]:
|
|
1262
1224
|
"""Reduce a function over the values of a set of mappings."""
|
|
1263
1225
|
chained = chain_mappings(*sequence)
|
|
1264
|
-
if
|
|
1265
|
-
func2 = cast("Callable[[
|
|
1226
|
+
if is_sentinel(initial):
|
|
1227
|
+
func2 = cast("Callable[[V, V], V]", func)
|
|
1266
1228
|
return {k: reduce(func2, v) for k, v in chained.items()}
|
|
1267
|
-
func2 = cast("Callable[[
|
|
1229
|
+
func2 = cast("Callable[[W, V], W]", func)
|
|
1268
1230
|
return {k: reduce(func2, v, initial) for k, v in chained.items()}
|
|
1269
1231
|
|
|
1270
1232
|
|
|
1271
1233
|
##
|
|
1272
1234
|
|
|
1273
1235
|
|
|
1274
|
-
def resolve_include_and_exclude(
|
|
1275
|
-
*,
|
|
1276
|
-
|
|
1277
|
-
exclude: MaybeIterable[_T] | None = None,
|
|
1278
|
-
) -> tuple[set[_T] | None, set[_T] | None]:
|
|
1236
|
+
def resolve_include_and_exclude[T](
|
|
1237
|
+
*, include: MaybeIterable[T] | None = None, exclude: MaybeIterable[T] | None = None
|
|
1238
|
+
) -> tuple[set[T] | None, set[T] | None]:
|
|
1279
1239
|
"""Resolve an inclusion/exclusion pair."""
|
|
1280
1240
|
include_use = include if include is None else set(always_iterable(include))
|
|
1281
1241
|
exclude_use = exclude if exclude is None else set(always_iterable(exclude))
|
|
@@ -1289,9 +1249,9 @@ def resolve_include_and_exclude(
|
|
|
1289
1249
|
|
|
1290
1250
|
|
|
1291
1251
|
@dataclass(kw_only=True, slots=True)
|
|
1292
|
-
class ResolveIncludeAndExcludeError(Exception
|
|
1293
|
-
include: Iterable[
|
|
1294
|
-
exclude: Iterable[
|
|
1252
|
+
class ResolveIncludeAndExcludeError[T](Exception):
|
|
1253
|
+
include: Iterable[T]
|
|
1254
|
+
exclude: Iterable[T]
|
|
1295
1255
|
|
|
1296
1256
|
@override
|
|
1297
1257
|
def __str__(self) -> str:
|
|
@@ -1304,7 +1264,7 @@ class ResolveIncludeAndExcludeError(Exception, Generic[_T]):
|
|
|
1304
1264
|
##
|
|
1305
1265
|
|
|
1306
1266
|
|
|
1307
|
-
def sort_iterable(iterable: Iterable[
|
|
1267
|
+
def sort_iterable[T](iterable: Iterable[T], /) -> list[T]:
|
|
1308
1268
|
"""Sort an iterable across types."""
|
|
1309
1269
|
return sorted(iterable, key=cmp_to_key(_sort_iterable_cmp))
|
|
1310
1270
|
|
|
@@ -1326,9 +1286,6 @@ def _sort_iterable_cmp(x: Any, y: Any, /) -> Sign:
|
|
|
1326
1286
|
if x is None:
|
|
1327
1287
|
y = cast("NoneType", y)
|
|
1328
1288
|
return 0
|
|
1329
|
-
if isinstance(x, dt.datetime):
|
|
1330
|
-
y = cast("dt.datetime", y)
|
|
1331
|
-
return _sort_iterable_cmp_datetimes(x, y)
|
|
1332
1289
|
if isinstance(x, float):
|
|
1333
1290
|
y = cast("float", y)
|
|
1334
1291
|
return _sort_iterable_cmp_floats(x, y)
|
|
@@ -1371,30 +1328,6 @@ class SortIterableError(Exception):
|
|
|
1371
1328
|
return f"Unable to sort {get_repr(self.x)} and {get_repr(self.y)}"
|
|
1372
1329
|
|
|
1373
1330
|
|
|
1374
|
-
def _sort_iterable_cmp_datetimes(x: dt.datetime, y: dt.datetime, /) -> Sign:
|
|
1375
|
-
"""Compare two datetimes."""
|
|
1376
|
-
match x.tzinfo, y.tzinfo:
|
|
1377
|
-
case None, None:
|
|
1378
|
-
return cast("Sign", (x > y) - (x < y))
|
|
1379
|
-
case dt.tzinfo(), None:
|
|
1380
|
-
return 1
|
|
1381
|
-
case None, dt.tzinfo():
|
|
1382
|
-
return -1
|
|
1383
|
-
case dt.tzinfo(), dt.tzinfo():
|
|
1384
|
-
x_utc = x.astimezone(tz=UTC)
|
|
1385
|
-
y_utc = y.astimezone(tz=UTC)
|
|
1386
|
-
result = cast("Sign", (x_utc > y_utc) - (x_utc < y_utc))
|
|
1387
|
-
if result != 0:
|
|
1388
|
-
return result
|
|
1389
|
-
x_time_zone = ensure_not_none(ensure_not_none(x.tzinfo).tzname(x))
|
|
1390
|
-
y_time_zone = ensure_not_none(ensure_not_none(y.tzinfo).tzname(y))
|
|
1391
|
-
return cast(
|
|
1392
|
-
"Sign", (x_time_zone > y_time_zone) - (x_time_zone < y_time_zone)
|
|
1393
|
-
)
|
|
1394
|
-
case _ as never:
|
|
1395
|
-
assert_never(never)
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
1331
|
def _sort_iterable_cmp_floats(x: float, y: float, /) -> Sign:
|
|
1399
1332
|
"""Compare two floats."""
|
|
1400
1333
|
x_nan, y_nan = map(isnan, [x, y])
|
|
@@ -1407,14 +1340,16 @@ def _sort_iterable_cmp_floats(x: float, y: float, /) -> Sign:
|
|
|
1407
1340
|
return -1
|
|
1408
1341
|
case False, False:
|
|
1409
1342
|
return cast("Sign", (x > y) - (x < y))
|
|
1410
|
-
case
|
|
1343
|
+
case never:
|
|
1411
1344
|
assert_never(never)
|
|
1412
1345
|
|
|
1413
1346
|
|
|
1414
1347
|
##
|
|
1415
1348
|
|
|
1416
1349
|
|
|
1417
|
-
def sum_mappings
|
|
1350
|
+
def sum_mappings[K: Hashable, V: SupportsAdd](
|
|
1351
|
+
*mappings: Mapping[K, V],
|
|
1352
|
+
) -> Mapping[K, V]:
|
|
1418
1353
|
"""Sum the values of a set of mappings."""
|
|
1419
1354
|
return reduce_mappings(add, mappings, initial=0)
|
|
1420
1355
|
|
|
@@ -1422,7 +1357,7 @@ def sum_mappings(*mappings: Mapping[_K, TSupportsAdd]) -> Mapping[_K, TSupportsA
|
|
|
1422
1357
|
##
|
|
1423
1358
|
|
|
1424
1359
|
|
|
1425
|
-
def take(n: int, iterable: Iterable[
|
|
1360
|
+
def take[T](n: int, iterable: Iterable[T], /) -> Sequence[T]:
|
|
1426
1361
|
"""Return first n items of the iterable as a list."""
|
|
1427
1362
|
return list(islice(iterable, n))
|
|
1428
1363
|
|
|
@@ -1431,23 +1366,23 @@ def take(n: int, iterable: Iterable[_T], /) -> Sequence[_T]:
|
|
|
1431
1366
|
|
|
1432
1367
|
|
|
1433
1368
|
@overload
|
|
1434
|
-
def transpose(iterable: Iterable[tuple[
|
|
1369
|
+
def transpose[T1](iterable: Iterable[tuple[T1]], /) -> tuple[list[T1]]: ...
|
|
1435
1370
|
@overload
|
|
1436
|
-
def transpose(
|
|
1437
|
-
iterable: Iterable[tuple[
|
|
1438
|
-
) -> tuple[list[
|
|
1371
|
+
def transpose[T1, T2](
|
|
1372
|
+
iterable: Iterable[tuple[T1, T2]], /
|
|
1373
|
+
) -> tuple[list[T1], list[T2]]: ...
|
|
1439
1374
|
@overload
|
|
1440
|
-
def transpose(
|
|
1441
|
-
iterable: Iterable[tuple[
|
|
1442
|
-
) -> tuple[list[
|
|
1375
|
+
def transpose[T1, T2, T3](
|
|
1376
|
+
iterable: Iterable[tuple[T1, T2, T3]], /
|
|
1377
|
+
) -> tuple[list[T1], list[T2], list[T3]]: ...
|
|
1443
1378
|
@overload
|
|
1444
|
-
def transpose(
|
|
1445
|
-
iterable: Iterable[tuple[
|
|
1446
|
-
) -> tuple[list[
|
|
1379
|
+
def transpose[T1, T2, T3, T4](
|
|
1380
|
+
iterable: Iterable[tuple[T1, T2, T3, T4]], /
|
|
1381
|
+
) -> tuple[list[T1], list[T2], list[T3], list[T4]]: ...
|
|
1447
1382
|
@overload
|
|
1448
|
-
def transpose(
|
|
1449
|
-
iterable: Iterable[tuple[
|
|
1450
|
-
) -> tuple[list[
|
|
1383
|
+
def transpose[T1, T2, T3, T4, T5](
|
|
1384
|
+
iterable: Iterable[tuple[T1, T2, T3, T4, T5]], /
|
|
1385
|
+
) -> tuple[list[T1], list[T2], list[T3], list[T4], list[T5]]: ...
|
|
1451
1386
|
def transpose(iterable: Iterable[tuple[Any]]) -> tuple[list[Any], ...]: # pyright: ignore[reportInconsistentOverload]
|
|
1452
1387
|
"""Typed verison of `transpose`."""
|
|
1453
1388
|
return tuple(map(list, zip(*iterable, strict=True)))
|
|
@@ -1456,9 +1391,9 @@ def transpose(iterable: Iterable[tuple[Any]]) -> tuple[list[Any], ...]: # pyrig
|
|
|
1456
1391
|
##
|
|
1457
1392
|
|
|
1458
1393
|
|
|
1459
|
-
def unique_everseen(
|
|
1460
|
-
iterable: Iterable[
|
|
1461
|
-
) -> Iterator[
|
|
1394
|
+
def unique_everseen[T](
|
|
1395
|
+
iterable: Iterable[T], /, *, key: Callable[[T], Any] | None = None
|
|
1396
|
+
) -> Iterator[T]:
|
|
1462
1397
|
"""Yield unique elements, preserving order."""
|
|
1463
1398
|
seenset = set()
|
|
1464
1399
|
seenset_add = seenset.add
|
|
@@ -1512,7 +1447,6 @@ __all__ = [
|
|
|
1512
1447
|
"ResolveIncludeAndExcludeError",
|
|
1513
1448
|
"SortIterableError",
|
|
1514
1449
|
"always_iterable",
|
|
1515
|
-
"always_iterable_hashable",
|
|
1516
1450
|
"apply_bijection",
|
|
1517
1451
|
"apply_to_tuple",
|
|
1518
1452
|
"apply_to_varargs",
|
|
@@ -1532,12 +1466,11 @@ __all__ = [
|
|
|
1532
1466
|
"check_unique_modulo_case",
|
|
1533
1467
|
"chunked",
|
|
1534
1468
|
"cmp_nullable",
|
|
1535
|
-
"ensure_hashables",
|
|
1536
1469
|
"ensure_iterable",
|
|
1537
1470
|
"ensure_iterable_not_str",
|
|
1471
|
+
"enumerate_with_edge",
|
|
1538
1472
|
"expanding_window",
|
|
1539
1473
|
"filter_include_and_exclude",
|
|
1540
|
-
"group_consecutive_integers",
|
|
1541
1474
|
"groupby_lists",
|
|
1542
1475
|
"hashable_to_iterable",
|
|
1543
1476
|
"is_iterable",
|
|
@@ -1560,6 +1493,5 @@ __all__ = [
|
|
|
1560
1493
|
"sum_mappings",
|
|
1561
1494
|
"take",
|
|
1562
1495
|
"transpose",
|
|
1563
|
-
"ungroup_consecutive_integers",
|
|
1564
1496
|
"unique_everseen",
|
|
1565
1497
|
]
|