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/functions.py
CHANGED
@@ -14,19 +14,8 @@ from types import (
|
|
14
14
|
MethodWrapperType,
|
15
15
|
WrapperDescriptorType,
|
16
16
|
)
|
17
|
-
from typing import
|
18
|
-
TYPE_CHECKING,
|
19
|
-
Any,
|
20
|
-
Generic,
|
21
|
-
Literal,
|
22
|
-
TypeGuard,
|
23
|
-
TypeVar,
|
24
|
-
cast,
|
25
|
-
overload,
|
26
|
-
override,
|
27
|
-
)
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal, TypeGuard, cast, overload, override
|
28
18
|
|
29
|
-
from typing_extensions import ParamSpec
|
30
19
|
from whenever import Date, PlainDateTime, Time, TimeDelta, ZonedDateTime
|
31
20
|
|
32
21
|
from utilities.reprlib import get_repr, get_repr_and_class
|
@@ -35,10 +24,7 @@ from utilities.types import (
|
|
35
24
|
Dataclass,
|
36
25
|
Number,
|
37
26
|
StrMapping,
|
38
|
-
|
39
|
-
TCallable1,
|
40
|
-
TCallable2,
|
41
|
-
TSupportsRichComparison,
|
27
|
+
SupportsRichComparison,
|
42
28
|
TupleOrStrMapping,
|
43
29
|
TypeLike,
|
44
30
|
)
|
@@ -47,27 +33,14 @@ if TYPE_CHECKING:
|
|
47
33
|
from collections.abc import Container, Hashable, Sized
|
48
34
|
|
49
35
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
_T2 = TypeVar("_T2")
|
54
|
-
_T3 = TypeVar("_T3")
|
55
|
-
_T4 = TypeVar("_T4")
|
56
|
-
_T5 = TypeVar("_T5")
|
57
|
-
_U = TypeVar("_U")
|
58
|
-
|
59
|
-
|
60
|
-
##
|
61
|
-
|
62
|
-
|
63
|
-
def apply_decorators(
|
64
|
-
func: TCallable1, /, *decorators: Callable[[TCallable2], TCallable2]
|
65
|
-
) -> TCallable1:
|
36
|
+
def apply_decorators[F1: Callable, F2: Callable](
|
37
|
+
func: F1, /, *decorators: Callable[[F2], F2]
|
38
|
+
) -> F1:
|
66
39
|
"""Apply a set of decorators to a function."""
|
67
40
|
return reduce(_apply_decorators_one, decorators, func)
|
68
41
|
|
69
42
|
|
70
|
-
def _apply_decorators_one(acc:
|
43
|
+
def _apply_decorators_one[F: Callable](acc: F, el: Callable[[Any], Any], /) -> F:
|
71
44
|
return el(acc)
|
72
45
|
|
73
46
|
|
@@ -125,66 +98,64 @@ class EnsureBytesError(Exception):
|
|
125
98
|
|
126
99
|
|
127
100
|
@overload
|
128
|
-
def ensure_class(obj: Any, cls: type[
|
101
|
+
def ensure_class[T](obj: Any, cls: type[T], /, *, nullable: bool) -> T | None: ...
|
129
102
|
@overload
|
130
|
-
def ensure_class(
|
131
|
-
obj: Any, cls: type[
|
132
|
-
) ->
|
103
|
+
def ensure_class[T](
|
104
|
+
obj: Any, cls: type[T], /, *, nullable: Literal[False] = False
|
105
|
+
) -> T: ...
|
133
106
|
@overload
|
134
|
-
def ensure_class(
|
135
|
-
obj: Any, cls: tuple[type[
|
136
|
-
) ->
|
107
|
+
def ensure_class[T1, T2](
|
108
|
+
obj: Any, cls: tuple[type[T1], type[T2]], /, *, nullable: bool
|
109
|
+
) -> T1 | T2 | None: ...
|
137
110
|
@overload
|
138
|
-
def ensure_class(
|
139
|
-
obj: Any, cls: tuple[type[
|
140
|
-
) ->
|
111
|
+
def ensure_class[T1, T2](
|
112
|
+
obj: Any, cls: tuple[type[T1], type[T2]], /, *, nullable: Literal[False] = False
|
113
|
+
) -> T1 | T2: ...
|
141
114
|
@overload
|
142
|
-
def ensure_class(
|
143
|
-
obj: Any, cls: tuple[type[
|
144
|
-
) ->
|
115
|
+
def ensure_class[T1, T2, T3](
|
116
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3]], /, *, nullable: bool
|
117
|
+
) -> T1 | T2 | T3 | None: ...
|
145
118
|
@overload
|
146
|
-
def ensure_class(
|
119
|
+
def ensure_class[T1, T2, T3](
|
147
120
|
obj: Any,
|
148
|
-
cls: tuple[type[
|
121
|
+
cls: tuple[type[T1], type[T2], type[T3]],
|
149
122
|
/,
|
150
123
|
*,
|
151
124
|
nullable: Literal[False] = False,
|
152
|
-
) ->
|
125
|
+
) -> T1 | T2 | T3: ...
|
153
126
|
@overload
|
154
|
-
def ensure_class(
|
155
|
-
obj: Any,
|
156
|
-
|
157
|
-
/,
|
158
|
-
*,
|
159
|
-
nullable: bool,
|
160
|
-
) -> _T1 | _T2 | _T3 | _T4 | None: ...
|
127
|
+
def ensure_class[T1, T2, T3, T4](
|
128
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4]], /, *, nullable: bool
|
129
|
+
) -> T1 | T2 | T3 | T4 | None: ...
|
161
130
|
@overload
|
162
|
-
def ensure_class(
|
131
|
+
def ensure_class[T1, T2, T3, T4](
|
163
132
|
obj: Any,
|
164
|
-
cls: tuple[type[
|
133
|
+
cls: tuple[type[T1], type[T2], type[T3], type[T4]],
|
165
134
|
/,
|
166
135
|
*,
|
167
136
|
nullable: Literal[False] = False,
|
168
|
-
) ->
|
137
|
+
) -> T1 | T2 | T3 | T4: ...
|
169
138
|
@overload
|
170
|
-
def ensure_class(
|
139
|
+
def ensure_class[T1, T2, T3, T4, T5](
|
171
140
|
obj: Any,
|
172
|
-
cls: tuple[type[
|
141
|
+
cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
|
173
142
|
/,
|
174
143
|
*,
|
175
144
|
nullable: bool,
|
176
|
-
) ->
|
145
|
+
) -> T1 | T2 | T3 | T4 | T5 | None: ...
|
177
146
|
@overload
|
178
|
-
def ensure_class(
|
147
|
+
def ensure_class[T1, T2, T3, T4, T5](
|
179
148
|
obj: Any,
|
180
|
-
cls: tuple[type[
|
149
|
+
cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
|
181
150
|
/,
|
182
151
|
*,
|
183
152
|
nullable: Literal[False] = False,
|
184
|
-
) ->
|
153
|
+
) -> T1 | T2 | T3 | T4 | T5: ...
|
185
154
|
@overload
|
186
|
-
def ensure_class
|
187
|
-
|
155
|
+
def ensure_class[T](
|
156
|
+
obj: Any, cls: TypeLike[T], /, *, nullable: bool = False
|
157
|
+
) -> Any: ...
|
158
|
+
def ensure_class[T](obj: Any, cls: TypeLike[T], /, *, nullable: bool = False) -> Any:
|
188
159
|
"""Ensure an object is of the required class."""
|
189
160
|
if isinstance(obj, cls) or ((obj is None) and nullable):
|
190
161
|
return obj
|
@@ -304,16 +275,16 @@ class EnsureIntError(Exception):
|
|
304
275
|
|
305
276
|
|
306
277
|
@overload
|
307
|
-
def ensure_member(
|
308
|
-
obj: Any, container: Container[
|
309
|
-
) ->
|
278
|
+
def ensure_member[T](
|
279
|
+
obj: Any, container: Container[T], /, *, nullable: bool
|
280
|
+
) -> T | None: ...
|
310
281
|
@overload
|
311
|
-
def ensure_member(
|
312
|
-
obj: Any, container: Container[
|
313
|
-
) ->
|
314
|
-
def ensure_member(
|
315
|
-
obj: Any, container: Container[
|
316
|
-
) ->
|
282
|
+
def ensure_member[T](
|
283
|
+
obj: Any, container: Container[T], /, *, nullable: Literal[False] = False
|
284
|
+
) -> T: ...
|
285
|
+
def ensure_member[T](
|
286
|
+
obj: Any, container: Container[T], /, *, nullable: bool = False
|
287
|
+
) -> T | None:
|
317
288
|
"""Ensure an object is a member of the container."""
|
318
289
|
if (obj in container) or ((obj is None) and nullable):
|
319
290
|
return obj
|
@@ -336,7 +307,7 @@ class EnsureMemberError(Exception):
|
|
336
307
|
##
|
337
308
|
|
338
309
|
|
339
|
-
def ensure_not_none(obj:
|
310
|
+
def ensure_not_none[T](obj: T | None, /, *, desc: str = "Object") -> T:
|
340
311
|
"""Ensure an object is not None."""
|
341
312
|
if obj is None:
|
342
313
|
raise EnsureNotNoneError(desc=desc)
|
@@ -578,7 +549,7 @@ class EnsureZonedDateTimeError(Exception):
|
|
578
549
|
##
|
579
550
|
|
580
551
|
|
581
|
-
def first(pair: tuple[
|
552
|
+
def first[T](pair: tuple[T, Any], /) -> T:
|
582
553
|
"""Get the first element in a pair."""
|
583
554
|
return pair[0]
|
584
555
|
|
@@ -587,10 +558,10 @@ def first(pair: tuple[_T, Any], /) -> _T:
|
|
587
558
|
|
588
559
|
|
589
560
|
@overload
|
590
|
-
def get_class(obj: type[
|
561
|
+
def get_class[T](obj: type[T], /) -> type[T]: ...
|
591
562
|
@overload
|
592
|
-
def get_class(obj:
|
593
|
-
def get_class(obj:
|
563
|
+
def get_class[T](obj: T, /) -> type[T]: ...
|
564
|
+
def get_class[T](obj: T | type[T], /) -> type[T]:
|
594
565
|
"""Get the class of an object, unless it is already a class."""
|
595
566
|
return obj if isinstance(obj, type) else type(obj)
|
596
567
|
|
@@ -654,7 +625,7 @@ def get_func_qualname(obj: Callable[..., Any], /) -> str:
|
|
654
625
|
##
|
655
626
|
|
656
627
|
|
657
|
-
def identity(obj:
|
628
|
+
def identity[T](obj: T, /) -> T:
|
658
629
|
"""Return the object itself."""
|
659
630
|
return obj
|
660
631
|
|
@@ -691,28 +662,30 @@ def is_hashable(obj: Any, /) -> TypeGuard[Hashable]:
|
|
691
662
|
|
692
663
|
|
693
664
|
@overload
|
694
|
-
def is_iterable_of(obj: Any, cls: type[
|
665
|
+
def is_iterable_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Iterable[T]]: ...
|
695
666
|
@overload
|
696
|
-
def is_iterable_of
|
667
|
+
def is_iterable_of[T1](
|
668
|
+
obj: Any, cls: tuple[type[T1]], /
|
669
|
+
) -> TypeGuard[Iterable[T1]]: ...
|
697
670
|
@overload
|
698
|
-
def is_iterable_of(
|
699
|
-
obj: Any, cls: tuple[type[
|
700
|
-
) -> TypeGuard[Iterable[
|
671
|
+
def is_iterable_of[T1, T2](
|
672
|
+
obj: Any, cls: tuple[type[T1], type[T2]], /
|
673
|
+
) -> TypeGuard[Iterable[T1 | T2]]: ...
|
701
674
|
@overload
|
702
|
-
def is_iterable_of(
|
703
|
-
obj: Any, cls: tuple[type[
|
704
|
-
) -> TypeGuard[Iterable[
|
675
|
+
def is_iterable_of[T1, T2, T3](
|
676
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3]], /
|
677
|
+
) -> TypeGuard[Iterable[T1 | T2 | T3]]: ...
|
705
678
|
@overload
|
706
|
-
def is_iterable_of(
|
707
|
-
obj: Any, cls: tuple[type[
|
708
|
-
) -> TypeGuard[Iterable[
|
679
|
+
def is_iterable_of[T1, T2, T3, T4](
|
680
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
681
|
+
) -> TypeGuard[Iterable[T1 | T2 | T3 | T4]]: ...
|
709
682
|
@overload
|
710
|
-
def is_iterable_of(
|
711
|
-
obj: Any, cls: tuple[type[
|
712
|
-
) -> TypeGuard[Iterable[
|
683
|
+
def is_iterable_of[T1, T2, T3, T4, T5](
|
684
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
685
|
+
) -> TypeGuard[Iterable[T1 | T2 | T3 | T4 | T5]]: ...
|
713
686
|
@overload
|
714
|
-
def is_iterable_of(obj: Any, cls: TypeLike[
|
715
|
-
def is_iterable_of(obj: Any, cls: TypeLike[
|
687
|
+
def is_iterable_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Iterable[T]]: ...
|
688
|
+
def is_iterable_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Iterable[T]]:
|
716
689
|
"""Check if an object is a iterable of tuple or string mappings."""
|
717
690
|
return isinstance(obj, Iterable) and all(map(make_isinstance(cls), obj))
|
718
691
|
|
@@ -737,28 +710,30 @@ def is_not_none(obj: Any, /) -> bool:
|
|
737
710
|
|
738
711
|
|
739
712
|
@overload
|
740
|
-
def is_sequence_of(obj: Any, cls: type[
|
713
|
+
def is_sequence_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Sequence[T]]: ...
|
741
714
|
@overload
|
742
|
-
def is_sequence_of
|
715
|
+
def is_sequence_of[T1](
|
716
|
+
obj: Any, cls: tuple[type[T1]], /
|
717
|
+
) -> TypeGuard[Sequence[T1]]: ...
|
743
718
|
@overload
|
744
|
-
def is_sequence_of(
|
745
|
-
obj: Any, cls: tuple[type[
|
746
|
-
) -> TypeGuard[Sequence[
|
719
|
+
def is_sequence_of[T1, T2](
|
720
|
+
obj: Any, cls: tuple[type[T1], type[T2]], /
|
721
|
+
) -> TypeGuard[Sequence[T1 | T2]]: ...
|
747
722
|
@overload
|
748
|
-
def is_sequence_of(
|
749
|
-
obj: Any, cls: tuple[type[
|
750
|
-
) -> TypeGuard[Sequence[
|
723
|
+
def is_sequence_of[T1, T2, T3](
|
724
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3]], /
|
725
|
+
) -> TypeGuard[Sequence[T1 | T2 | T3]]: ...
|
751
726
|
@overload
|
752
|
-
def is_sequence_of(
|
753
|
-
obj: Any, cls: tuple[type[
|
754
|
-
) -> TypeGuard[Sequence[
|
727
|
+
def is_sequence_of[T1, T2, T3, T4](
|
728
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
729
|
+
) -> TypeGuard[Sequence[T1 | T2 | T3 | T4]]: ...
|
755
730
|
@overload
|
756
|
-
def is_sequence_of(
|
757
|
-
obj: Any, cls: tuple[type[
|
758
|
-
) -> TypeGuard[Sequence[
|
731
|
+
def is_sequence_of[T1, T2, T3, T4, T5](
|
732
|
+
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
733
|
+
) -> TypeGuard[Sequence[T1 | T2 | T3 | T4 | T5]]: ...
|
759
734
|
@overload
|
760
|
-
def is_sequence_of(obj: Any, cls: TypeLike[
|
761
|
-
def is_sequence_of(obj: Any, cls: TypeLike[
|
735
|
+
def is_sequence_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Sequence[T]]: ...
|
736
|
+
def is_sequence_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Sequence[T]]:
|
762
737
|
"""Check if an object is a sequence of tuple or string mappings."""
|
763
738
|
return isinstance(obj, Sequence) and is_iterable_of(obj, cls)
|
764
739
|
|
@@ -821,46 +796,42 @@ def is_tuple_or_str_mapping(obj: Any, /) -> TypeGuard[TupleOrStrMapping]:
|
|
821
796
|
|
822
797
|
|
823
798
|
@overload
|
824
|
-
def make_isinstance(cls: type[
|
799
|
+
def make_isinstance[T](cls: type[T], /) -> Callable[[Any], TypeGuard[T]]: ...
|
825
800
|
@overload
|
826
|
-
def make_isinstance(cls: tuple[type[
|
801
|
+
def make_isinstance[T1](cls: tuple[type[T1]], /) -> Callable[[Any], TypeGuard[T1]]: ...
|
827
802
|
@overload
|
828
|
-
def make_isinstance(
|
829
|
-
cls: tuple[type[
|
830
|
-
) -> Callable[[Any], TypeGuard[
|
803
|
+
def make_isinstance[T1, T2](
|
804
|
+
cls: tuple[type[T1], type[T2]], /
|
805
|
+
) -> Callable[[Any], TypeGuard[T1 | T2]]: ...
|
831
806
|
@overload
|
832
|
-
def make_isinstance(
|
833
|
-
cls: tuple[type[
|
834
|
-
) -> Callable[[Any], TypeGuard[
|
807
|
+
def make_isinstance[T1, T2, T3](
|
808
|
+
cls: tuple[type[T1], type[T2], type[T3]], /
|
809
|
+
) -> Callable[[Any], TypeGuard[T1 | T2 | T3]]: ...
|
835
810
|
@overload
|
836
|
-
def make_isinstance(
|
837
|
-
cls: tuple[type[
|
838
|
-
) -> Callable[[Any], TypeGuard[
|
811
|
+
def make_isinstance[T1, T2, T3, T4](
|
812
|
+
cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
813
|
+
) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4]]: ...
|
839
814
|
@overload
|
840
|
-
def make_isinstance(
|
841
|
-
cls: tuple[type[
|
842
|
-
) -> Callable[[Any], TypeGuard[
|
815
|
+
def make_isinstance[T1, T2, T3, T4, T5](
|
816
|
+
cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
817
|
+
) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4 | T5]]: ...
|
843
818
|
@overload
|
844
|
-
def make_isinstance(cls: TypeLike[
|
845
|
-
def make_isinstance(cls: TypeLike[
|
819
|
+
def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]: ...
|
820
|
+
def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]:
|
846
821
|
"""Make a curried `isinstance` function."""
|
847
822
|
return partial(_make_instance_core, cls=cls)
|
848
823
|
|
849
824
|
|
850
|
-
def _make_instance_core(obj: Any, /, *, cls: TypeLike[
|
825
|
+
def _make_instance_core[T](obj: Any, /, *, cls: TypeLike[T]) -> TypeGuard[T]:
|
851
826
|
return isinstance(obj, cls)
|
852
827
|
|
853
828
|
|
854
829
|
##
|
855
830
|
|
856
831
|
|
857
|
-
def map_object(
|
858
|
-
func: Callable[[Any], Any],
|
859
|
-
|
860
|
-
/,
|
861
|
-
*,
|
862
|
-
before: Callable[[Any], Any] | None = None,
|
863
|
-
) -> _T:
|
832
|
+
def map_object[T](
|
833
|
+
func: Callable[[Any], Any], obj: T, /, *, before: Callable[[Any], Any] | None = None
|
834
|
+
) -> T:
|
864
835
|
"""Map a function over an object, across a variety of structures."""
|
865
836
|
if before is not None:
|
866
837
|
obj = before(obj)
|
@@ -881,19 +852,16 @@ def map_object(
|
|
881
852
|
|
882
853
|
|
883
854
|
@overload
|
884
|
-
def min_nullable(
|
885
|
-
iterable: Iterable[
|
886
|
-
) ->
|
855
|
+
def min_nullable[T: SupportsRichComparison](
|
856
|
+
iterable: Iterable[T | None], /, *, default: Sentinel = ...
|
857
|
+
) -> T: ...
|
887
858
|
@overload
|
888
|
-
def min_nullable(
|
889
|
-
iterable: Iterable[
|
890
|
-
) ->
|
891
|
-
def min_nullable(
|
892
|
-
iterable: Iterable[
|
893
|
-
|
894
|
-
*,
|
895
|
-
default: _U | Sentinel = sentinel,
|
896
|
-
) -> TSupportsRichComparison | _U:
|
859
|
+
def min_nullable[T: SupportsRichComparison, U](
|
860
|
+
iterable: Iterable[T | None], /, *, default: U = ...
|
861
|
+
) -> T | U: ...
|
862
|
+
def min_nullable[T: SupportsRichComparison, U](
|
863
|
+
iterable: Iterable[T | None], /, *, default: U | Sentinel = sentinel
|
864
|
+
) -> T | U:
|
897
865
|
"""Compute the minimum of a set of values; ignoring nulls."""
|
898
866
|
values = (i for i in iterable if i is not None)
|
899
867
|
if isinstance(default, Sentinel):
|
@@ -905,8 +873,8 @@ def min_nullable(
|
|
905
873
|
|
906
874
|
|
907
875
|
@dataclass(kw_only=True, slots=True)
|
908
|
-
class MinNullableError(Exception
|
909
|
-
values: Iterable[
|
876
|
+
class MinNullableError[T: SupportsRichComparison](Exception):
|
877
|
+
values: Iterable[T]
|
910
878
|
|
911
879
|
@override
|
912
880
|
def __str__(self) -> str:
|
@@ -914,19 +882,16 @@ class MinNullableError(Exception, Generic[TSupportsRichComparison]):
|
|
914
882
|
|
915
883
|
|
916
884
|
@overload
|
917
|
-
def max_nullable(
|
918
|
-
iterable: Iterable[
|
919
|
-
) ->
|
885
|
+
def max_nullable[T: SupportsRichComparison](
|
886
|
+
iterable: Iterable[T | None], /, *, default: Sentinel = ...
|
887
|
+
) -> T: ...
|
920
888
|
@overload
|
921
|
-
def max_nullable(
|
922
|
-
iterable: Iterable[
|
923
|
-
) ->
|
924
|
-
def max_nullable(
|
925
|
-
iterable: Iterable[
|
926
|
-
|
927
|
-
*,
|
928
|
-
default: _U | Sentinel = sentinel,
|
929
|
-
) -> TSupportsRichComparison | _U:
|
889
|
+
def max_nullable[T: SupportsRichComparison, U](
|
890
|
+
iterable: Iterable[T | None], /, *, default: U = ...
|
891
|
+
) -> T | U: ...
|
892
|
+
def max_nullable[T: SupportsRichComparison, U](
|
893
|
+
iterable: Iterable[T | None], /, *, default: U | Sentinel = sentinel
|
894
|
+
) -> T | U:
|
930
895
|
"""Compute the maximum of a set of values; ignoring nulls."""
|
931
896
|
values = (i for i in iterable if i is not None)
|
932
897
|
if isinstance(default, Sentinel):
|
@@ -938,7 +903,7 @@ def max_nullable(
|
|
938
903
|
|
939
904
|
|
940
905
|
@dataclass(kw_only=True, slots=True)
|
941
|
-
class MaxNullableError
|
906
|
+
class MaxNullableError[TSupportsRichComparison](Exception):
|
942
907
|
values: Iterable[TSupportsRichComparison]
|
943
908
|
|
944
909
|
@override
|
@@ -949,11 +914,11 @@ class MaxNullableError(Exception, Generic[TSupportsRichComparison]):
|
|
949
914
|
##
|
950
915
|
|
951
916
|
|
952
|
-
def not_func(func: Callable[
|
917
|
+
def not_func[**P](func: Callable[P, bool], /) -> Callable[P, bool]:
|
953
918
|
"""Lift a boolean-valued function to return its conjugation."""
|
954
919
|
|
955
920
|
@wraps(func)
|
956
|
-
def wrapped(*args:
|
921
|
+
def wrapped(*args: P.args, **kwargs: P.kwargs) -> bool:
|
957
922
|
return not func(*args, **kwargs)
|
958
923
|
|
959
924
|
return wrapped
|
@@ -962,7 +927,7 @@ def not_func(func: Callable[_P, bool], /) -> Callable[_P, bool]:
|
|
962
927
|
##
|
963
928
|
|
964
929
|
|
965
|
-
def second(pair: tuple[Any,
|
930
|
+
def second[U](pair: tuple[Any, U], /) -> U:
|
966
931
|
"""Get the second element in a pair."""
|
967
932
|
return pair[1]
|
968
933
|
|
utilities/functools.py
CHANGED
@@ -3,19 +3,15 @@ from __future__ import annotations
|
|
3
3
|
from functools import cache as _cache
|
4
4
|
from functools import lru_cache as _lru_cache
|
5
5
|
from functools import partial as _partial
|
6
|
-
from typing import TYPE_CHECKING, Any,
|
6
|
+
from typing import TYPE_CHECKING, Any, cast, overload, override
|
7
7
|
|
8
8
|
if TYPE_CHECKING:
|
9
9
|
from collections.abc import Callable
|
10
10
|
|
11
|
-
from utilities.types import TCallable
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def cache(func: TCallable, /) -> TCallable:
|
12
|
+
def cache[F: Callable](func: F, /) -> F:
|
17
13
|
"""Typed version of `cache`."""
|
18
|
-
typed_cache = cast("Callable[[
|
14
|
+
typed_cache = cast("Callable[[F], F]", _cache)
|
19
15
|
return typed_cache(func)
|
20
16
|
|
21
17
|
|
@@ -23,20 +19,20 @@ def cache(func: TCallable, /) -> TCallable:
|
|
23
19
|
|
24
20
|
|
25
21
|
@overload
|
26
|
-
def lru_cache(
|
27
|
-
func:
|
28
|
-
) ->
|
22
|
+
def lru_cache[F: Callable](
|
23
|
+
func: F, /, *, max_size: int = ..., typed: bool = ...
|
24
|
+
) -> F: ...
|
29
25
|
@overload
|
30
|
-
def lru_cache(
|
26
|
+
def lru_cache[F: Callable](
|
31
27
|
func: None = None, /, *, max_size: int = ..., typed: bool = ...
|
32
|
-
) -> Callable[[
|
33
|
-
def lru_cache(
|
34
|
-
func:
|
35
|
-
) ->
|
28
|
+
) -> Callable[[F], F]: ...
|
29
|
+
def lru_cache[F: Callable](
|
30
|
+
func: F | None = None, /, *, max_size: int = 128, typed: bool = False
|
31
|
+
) -> F | Callable[[F], F]:
|
36
32
|
"""Typed version of `lru_cache`."""
|
37
33
|
if func is None:
|
38
34
|
result = partial(lru_cache, max_size=max_size, typed=typed)
|
39
|
-
return cast("Callable[[
|
35
|
+
return cast("Callable[[F], F]", result)
|
40
36
|
wrapped = _lru_cache(maxsize=max_size, typed=typed)(func)
|
41
37
|
return cast("Any", wrapped)
|
42
38
|
|
@@ -44,11 +40,11 @@ def lru_cache(
|
|
44
40
|
##
|
45
41
|
|
46
42
|
|
47
|
-
class partial(_partial[
|
43
|
+
class partial[T](_partial[T]): # noqa: N801
|
48
44
|
"""Partial which accepts Ellipsis for positional arguments."""
|
49
45
|
|
50
46
|
@override
|
51
|
-
def __call__(self, *args: Any, **kwargs: Any) ->
|
47
|
+
def __call__(self, *args: Any, **kwargs: Any) -> T:
|
52
48
|
iter_args = iter(args)
|
53
49
|
head = (next(iter_args) if arg is ... else arg for arg in self.args)
|
54
50
|
return self.func(*head, *iter_args, **{**self.keywords, **kwargs})
|