dycw-utilities 0.133.7__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.
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
- TCallable,
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
- _P = ParamSpec("_P")
51
- _T = TypeVar("_T")
52
- _T1 = TypeVar("_T1")
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: TCallable, el: Callable[[Any], Any], /) -> TCallable:
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[_T], /, *, nullable: bool) -> _T | None: ...
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[_T], /, *, nullable: Literal[False] = False
132
- ) -> _T: ...
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[_T1], type[_T2]], /, *, nullable: bool
136
- ) -> _T1 | _T2 | None: ...
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[_T1], type[_T2]], /, *, nullable: Literal[False] = False
140
- ) -> _T1 | _T2: ...
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[_T1], type[_T2], type[_T3]], /, *, nullable: bool
144
- ) -> _T1 | _T2 | _T3 | None: ...
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[_T1], type[_T2], type[_T3]],
121
+ cls: tuple[type[T1], type[T2], type[T3]],
149
122
  /,
150
123
  *,
151
124
  nullable: Literal[False] = False,
152
- ) -> _T1 | _T2 | _T3: ...
125
+ ) -> T1 | T2 | T3: ...
153
126
  @overload
154
- def ensure_class(
155
- obj: Any,
156
- cls: tuple[type[_T1], type[_T2], type[_T3], type[_T4]],
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[_T1], type[_T2], type[_T3], type[_T4]],
133
+ cls: tuple[type[T1], type[T2], type[T3], type[T4]],
165
134
  /,
166
135
  *,
167
136
  nullable: Literal[False] = False,
168
- ) -> _T1 | _T2 | _T3 | _T4: ...
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[_T1], type[_T2], type[_T3], type[_T4], type[_T5]],
141
+ cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
173
142
  /,
174
143
  *,
175
144
  nullable: bool,
176
- ) -> _T1 | _T2 | _T3 | _T4 | _T5 | None: ...
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[_T1], type[_T2], type[_T3], type[_T4], type[_T5]],
149
+ cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
181
150
  /,
182
151
  *,
183
152
  nullable: Literal[False] = False,
184
- ) -> _T1 | _T2 | _T3 | _T4 | _T5: ...
153
+ ) -> T1 | T2 | T3 | T4 | T5: ...
185
154
  @overload
186
- def ensure_class(obj: Any, cls: TypeLike[_T], /, *, nullable: bool = False) -> Any: ...
187
- def ensure_class(obj: Any, cls: TypeLike[_T], /, *, nullable: bool = False) -> Any:
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[_T], /, *, nullable: bool
309
- ) -> _T | None: ...
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[_T], /, *, nullable: Literal[False] = False
313
- ) -> _T: ...
314
- def ensure_member(
315
- obj: Any, container: Container[_T], /, *, nullable: bool = False
316
- ) -> _T | None:
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: _T | None, /, *, desc: str = "Object") -> _T:
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[_T, Any], /) -> _T:
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[_T], /) -> type[_T]: ...
561
+ def get_class[T](obj: type[T], /) -> type[T]: ...
591
562
  @overload
592
- def get_class(obj: _T, /) -> type[_T]: ...
593
- def get_class(obj: _T | type[_T], /) -> type[_T]:
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: _T, /) -> _T:
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[_T], /) -> TypeGuard[Iterable[_T]]: ...
665
+ def is_iterable_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Iterable[T]]: ...
695
666
  @overload
696
- def is_iterable_of(obj: Any, cls: tuple[type[_T1]], /) -> TypeGuard[Iterable[_T1]]: ...
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[_T1], type[_T2]], /
700
- ) -> TypeGuard[Iterable[_T1 | _T2]]: ...
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[_T1], type[_T2], type[_T3]], /
704
- ) -> TypeGuard[Iterable[_T1 | _T2 | _T3]]: ...
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[_T1], type[_T2], type[_T3], type[_T4]], /
708
- ) -> TypeGuard[Iterable[_T1 | _T2 | _T3 | _T4]]: ...
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[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
712
- ) -> TypeGuard[Iterable[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
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[_T], /) -> TypeGuard[Iterable[_T]]: ...
715
- def is_iterable_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Iterable[_T]]:
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[_T], /) -> TypeGuard[Sequence[_T]]: ...
713
+ def is_sequence_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Sequence[T]]: ...
741
714
  @overload
742
- def is_sequence_of(obj: Any, cls: tuple[type[_T1]], /) -> TypeGuard[Sequence[_T1]]: ...
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[_T1], type[_T2]], /
746
- ) -> TypeGuard[Sequence[_T1 | _T2]]: ...
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[_T1], type[_T2], type[_T3]], /
750
- ) -> TypeGuard[Sequence[_T1 | _T2 | _T3]]: ...
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[_T1], type[_T2], type[_T3], type[_T4]], /
754
- ) -> TypeGuard[Sequence[_T1 | _T2 | _T3 | _T4]]: ...
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[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
758
- ) -> TypeGuard[Sequence[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
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[_T], /) -> TypeGuard[Sequence[_T]]: ...
761
- def is_sequence_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Sequence[_T]]:
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[_T], /) -> Callable[[Any], TypeGuard[_T]]: ...
799
+ def make_isinstance[T](cls: type[T], /) -> Callable[[Any], TypeGuard[T]]: ...
825
800
  @overload
826
- def make_isinstance(cls: tuple[type[_T1]], /) -> Callable[[Any], TypeGuard[_T1]]: ...
801
+ def make_isinstance[T1](cls: tuple[type[T1]], /) -> Callable[[Any], TypeGuard[T1]]: ...
827
802
  @overload
828
- def make_isinstance(
829
- cls: tuple[type[_T1], type[_T2]], /
830
- ) -> Callable[[Any], TypeGuard[_T1 | _T2]]: ...
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[_T1], type[_T2], type[_T3]], /
834
- ) -> Callable[[Any], TypeGuard[_T1 | _T2 | _T3]]: ...
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[_T1], type[_T2], type[_T3], type[_T4]], /
838
- ) -> Callable[[Any], TypeGuard[_T1 | _T2 | _T3 | _T4]]: ...
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[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
842
- ) -> Callable[[Any], TypeGuard[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
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[_T], /) -> Callable[[Any], TypeGuard[_T]]: ...
845
- def make_isinstance(cls: TypeLike[_T], /) -> Callable[[Any], TypeGuard[_T]]:
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[_T]) -> TypeGuard[_T]:
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
- obj: _T,
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[TSupportsRichComparison | None], /, *, default: Sentinel = ...
886
- ) -> TSupportsRichComparison: ...
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[TSupportsRichComparison | None], /, *, default: _U = ...
890
- ) -> TSupportsRichComparison | _U: ...
891
- def min_nullable(
892
- iterable: Iterable[TSupportsRichComparison | None],
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, Generic[TSupportsRichComparison]):
909
- values: Iterable[TSupportsRichComparison]
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[TSupportsRichComparison | None], /, *, default: Sentinel = ...
919
- ) -> TSupportsRichComparison: ...
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[TSupportsRichComparison | None], /, *, default: _U = ...
923
- ) -> TSupportsRichComparison | _U: ...
924
- def max_nullable(
925
- iterable: Iterable[TSupportsRichComparison | None],
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(Exception, Generic[TSupportsRichComparison]):
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[_P, bool], /) -> Callable[_P, bool]:
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: _P.args, **kwargs: _P.kwargs) -> bool:
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, _U], /) -> _U:
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, TypeVar, cast, overload, override
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
- _T = TypeVar("_T")
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[[TCallable], TCallable]", _cache)
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: TCallable, /, *, max_size: int = ..., typed: bool = ...
28
- ) -> TCallable: ...
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[[TCallable], TCallable]: ...
33
- def lru_cache(
34
- func: TCallable | None = None, /, *, max_size: int = 128, typed: bool = False
35
- ) -> TCallable | Callable[[TCallable], TCallable]:
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[[TCallable], TCallable]", result)
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[_T]): # noqa: N801
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) -> _T:
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})