dycw-utilities 0.110.5__py3-none-any.whl → 0.110.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.110.5
3
+ Version: 0.110.7
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=euqVJWl3d79ucm4qx3fmZY5DDLrmY7CHTJV8enITT7M,60
1
+ utilities/__init__.py,sha256=RH75kOPQZPuI3PzW-iy4R8ZfTSbKQHPjZwc14TPFLN8,60
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
4
4
  utilities/asyncio.py,sha256=41oQUurWMvadFK5gFnaG21hMM0Vmfn2WS6OpC0R9mas,14757
@@ -15,10 +15,10 @@ utilities/dataclasses.py,sha256=Q197PVnE_vUMn_SNnqJBCo4eRy4bdHtgMHWRbSJPtFk,2667
15
15
  utilities/datetime.py,sha256=GOs-MIEW_A49kzqa1yhIoeNeSqqPVgGO-h2AThtgTDk,37326
16
16
  utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
17
17
  utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
18
- utilities/eventkit.py,sha256=bG2rjgQqPaaOEW879Pc8vOCX6zRAl1frIhB1Y6fqQXg,13149
18
+ utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
19
19
  utilities/fastapi.py,sha256=uwqOGbGwzIbP-lfm-ApG1ZEN3BA_TDsaiuTghhLmxb8,2413
20
20
  utilities/fpdf2.py,sha256=zM3gwOYcAfv7P4qhbyvzPmRY4PPAiAQ-ZnPC6I9SZ1M,1832
21
- utilities/functions.py,sha256=BH4F_X34tqHuk-BzG9lzooYIP1OmVKm6GQw51qqYShM,27461
21
+ utilities/functions.py,sha256=lAJeERNrmcpwon3drcTIlizLVRd8D-gdXojxtKFN0LM,28736
22
22
  utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
23
23
  utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
24
24
  utilities/git.py,sha256=wpt5dZ5Oi5931pN24_VLZYaQOvmR0OcQuVtgHzFUN1k,2359
@@ -34,14 +34,14 @@ utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
34
34
  utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
35
35
  utilities/math.py,sha256=TexfvLCI12d9Sw5_W4pKVBZ3nRr3zk2iPkcEU7xdEWU,26771
36
36
  utilities/memory_profiler.py,sha256=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
37
- utilities/modules.py,sha256=SnhsRHRUS1po_acejrINauihGQpPvVsp8RDNCei1OLQ,3173
37
+ utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
38
38
  utilities/more_itertools.py,sha256=CPUxrMAcTwRxbzbhiqPKi3Xx9hxqI0t6gkWjutaibGk,5534
39
39
  utilities/numpy.py,sha256=cBgCBet8YfZP_rb4nkCJHZx9_03qPEinVENMk1dGVYQ,25683
40
40
  utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
41
41
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
42
42
  utilities/orjson.py,sha256=Wj5pzG_VdgoAy14a7Luhem-BgYrRtRFvvl_POiszRd0,36930
43
43
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
44
- utilities/parse.py,sha256=zfvVczg4wQUvjgCEa_y36LSeY9WFbsdibI3n4S7_gzA,19076
44
+ utilities/parse.py,sha256=fki2mnPGa1Ex-aeWiXDkUBHWb7FEk4F6AzMiHjqHXdw,19081
45
45
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
46
46
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
47
47
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
@@ -52,13 +52,13 @@ utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
52
52
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
54
54
  utilities/pyinstrument.py,sha256=ROq2txPwbe2ZUuYJ2IDNbfT97lu2ca0v5_C_yn6sSlM,800
55
- utilities/pyrsistent.py,sha256=TLJfiiKO4cKNU_pCoM3zDqmSM421qpuoaeaBNnyC_Ac,2489
55
+ utilities/pyrsistent.py,sha256=MoDcAqQGlSNkmlS32DCJLw-cZFAfHB6K9kpox_iyI4k,2512
56
56
  utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
57
57
  utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq3c,5088
58
58
  utilities/python_dotenv.py,sha256=6viKAI7zx9YQU2ewITaOcad7wMwkrf6FbYpBmCl4vCA,3170
59
59
  utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
60
60
  utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
61
- utilities/redis.py,sha256=CsDQqc9V6ASLzLQwtbQXZQEndyG9pJiCOhPlPeszt7Y,21203
61
+ utilities/redis.py,sha256=fAUbfOlCmxcxhh47PXQX63w0CU5iOFKfdUJ7jDn9ntM,22096
62
62
  utilities/reprlib.py,sha256=Re9bk3n-kC__9DxQmRlevqFA86pE6TtVfWjUgpbVOv0,1849
63
63
  utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
64
64
  utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
@@ -77,17 +77,17 @@ utilities/text.py,sha256=hfcBKF22fKT6s_U-ZdP-g5TjFQ0-NrIrQdvIwERWT80,10971
77
77
  utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
78
78
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
79
79
  utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
80
- utilities/types.py,sha256=bDzOlJ1cOiHoP2Ken0Z-xLD2eS6ENrYXNkU9kCQ6-8o,18058
80
+ utilities/types.py,sha256=kVY71hZkcnyYNIlYSse0mLm8yeP3OBkzhDPMME6jXxo,18126
81
81
  utilities/typing.py,sha256=gLg4EbE1FX52fJ1d3ji4i08qolwu9qgWt8w_w_Y5DTk,5512
82
82
  utilities/tzdata.py,sha256=2ZsPmhTVM9Ptrxb4QrWKtKOB9RiH8IOO-A1u7ULdVbg,176
83
83
  utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
84
84
  utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
85
85
  utilities/version.py,sha256=QFuyEeQA6jI0ruBEcmhqG36f-etg1AEiD1drBBqhQrs,5358
86
- utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
86
+ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
87
87
  utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
89
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
90
- dycw_utilities-0.110.5.dist-info/METADATA,sha256=uaH8NHlolCoxKBFqvnHmO0KRPWTk41ramlnv9yqzaEM,13004
91
- dycw_utilities-0.110.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.110.5.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.110.5.dist-info/RECORD,,
90
+ dycw_utilities-0.110.7.dist-info/METADATA,sha256=wFw9aZuOwiyPR1o4QtTmz5fPx9y5gBbKheuvV0XKhA8,13004
91
+ dycw_utilities-0.110.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.110.7.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.110.7.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.110.5"
3
+ __version__ = "0.110.7"
utilities/eventkit.py CHANGED
@@ -39,7 +39,7 @@ from eventkit import (
39
39
  from utilities.functions import apply_decorators
40
40
  from utilities.iterables import always_iterable
41
41
  from utilities.logging import get_logger
42
- from utilities.types import TCallable, TCallableMaybeCoroutine1None
42
+ from utilities.types import TCallable, TCallableMaybeCoroutine1None, TypeLike
43
43
 
44
44
  if TYPE_CHECKING:
45
45
  from collections.abc import Callable
@@ -59,7 +59,7 @@ def add_listener(
59
59
  /,
60
60
  *,
61
61
  error: Callable[[Event, BaseException], MaybeCoroutine1[None]] | None = None,
62
- ignore: type[BaseException] | tuple[type[BaseException], ...] | None = None,
62
+ ignore: TypeLike[BaseException] | None = None,
63
63
  logger: LoggerOrName | None = None,
64
64
  decorators: MaybeIterable[Callable[[TCallable], TCallable]] | None = None,
65
65
  done: Callable[..., MaybeCoroutine1[None]] | None = None,
@@ -104,7 +104,7 @@ class LiftedEvent(Generic[TCallableMaybeCoroutine1None]):
104
104
  /,
105
105
  *,
106
106
  error: Callable[[Event, BaseException], MaybeCoroutine1[None]] | None = None,
107
- ignore: type[BaseException] | tuple[type[BaseException], ...] | None = None,
107
+ ignore: TypeLike[BaseException] | None = None,
108
108
  logger: LoggerOrName | None = None,
109
109
  decorators: MaybeIterable[Callable[[TCallable], TCallable]] | None = None,
110
110
  done: Callable[..., MaybeCoroutine1[None]] | None = None,
@@ -267,7 +267,7 @@ class TypedEvent(Event, Generic[TCallableMaybeCoroutine1None]):
267
267
  done: Callable[[Self], MaybeCoroutine1[None]] | None = None,
268
268
  keep_ref: bool = False,
269
269
  *,
270
- ignore: type[BaseException] | tuple[type[BaseException], ...] | None = None,
270
+ ignore: TypeLike[BaseException] | None = None,
271
271
  logger: LoggerOrName | None = None,
272
272
  decorators: MaybeIterable[Callable[[TCallable], TCallable]] | None = None,
273
273
  ) -> Self:
@@ -295,7 +295,7 @@ def lift_listener(
295
295
  /,
296
296
  *,
297
297
  error: Callable[[Event, BaseException], MaybeCoroutine1[None]] | None = None,
298
- ignore: type[BaseException] | tuple[type[BaseException], ...] | None = None,
298
+ ignore: TypeLike[BaseException] | None = None,
299
299
  logger: LoggerOrName | None = None,
300
300
  decorators: MaybeIterable[Callable[[TCallable], TCallable]] | None = None,
301
301
  ) -> Callable[..., MaybeCoroutine1[None]]:
utilities/functions.py CHANGED
@@ -22,6 +22,7 @@ from typing import (
22
22
  Literal,
23
23
  TypeGuard,
24
24
  TypeVar,
25
+ assert_never,
25
26
  cast,
26
27
  overload,
27
28
  override,
@@ -40,6 +41,7 @@ from utilities.types import (
40
41
  TCallable2,
41
42
  TSupportsRichComparison,
42
43
  TupleOrStrMapping,
44
+ TypeLike,
43
45
  )
44
46
 
45
47
  if TYPE_CHECKING:
@@ -181,9 +183,9 @@ def ensure_class(
181
183
  *,
182
184
  nullable: Literal[False] = False,
183
185
  ) -> _T1 | _T2 | _T3 | _T4 | _T5: ...
184
- def ensure_class(
185
- obj: Any, cls: type[_T] | tuple[type[_T], ...], /, *, nullable: bool = False
186
- ) -> Any:
186
+ @overload
187
+ def ensure_class(obj: Any, cls: TypeLike[_T], /, *, nullable: bool = False) -> Any: ...
188
+ def ensure_class(obj: Any, cls: TypeLike[_T], /, *, nullable: bool = False) -> Any:
187
189
  """Ensure an object is of the required class."""
188
190
  if isinstance(obj, cls) or ((obj is None) and nullable):
189
191
  return obj
@@ -193,7 +195,7 @@ def ensure_class(
193
195
  @dataclass(kw_only=True, slots=True)
194
196
  class EnsureClassError(Exception):
195
197
  obj: Any
196
- cls: type[Any] | tuple[type[Any], ...]
198
+ cls: TypeLike[Any]
197
199
  nullable: bool
198
200
 
199
201
  @override
@@ -658,9 +660,23 @@ def is_hashable(obj: Any, /) -> TypeGuard[Hashable]:
658
660
  ##
659
661
 
660
662
 
661
- def is_instance_int_not_bool(obj: Any, /) -> TypeGuard[int]:
662
- """Check if an object is an integer, and not a boolean."""
663
- return isinstance(obj, int) and not isinstance(obj, bool)
663
+ def is_instance_not_bool_int(
664
+ obj: Any, class_or_tuple: TypeLike[Any], /
665
+ ) -> TypeGuard[int]:
666
+ """Check if an instance relationship holds, except bool<int."""
667
+ match class_or_tuple:
668
+ case type() as type_:
669
+ return _is_instance_not_bool_int(obj, type_)
670
+ case tuple() as types:
671
+ return any(_is_instance_not_bool_int(obj, p) for p in types)
672
+ case _ as never:
673
+ assert_never(never)
674
+
675
+
676
+ def _is_instance_not_bool_int(obj: Any, type_: type[Any], /) -> bool:
677
+ return isinstance(obj, type_) and not (
678
+ isinstance(obj, bool) and issubclass(type_, int) and not issubclass(type_, bool)
679
+ )
664
680
 
665
681
 
666
682
  ##
@@ -686,7 +702,9 @@ def is_iterable_of(
686
702
  def is_iterable_of(
687
703
  obj: Any, cls: tuple[type[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
688
704
  ) -> TypeGuard[Iterable[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
689
- def is_iterable_of(obj: Any, cls: Any, /) -> TypeGuard[Iterable[Any]]:
705
+ @overload
706
+ def is_iterable_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Iterable[_T]]: ...
707
+ def is_iterable_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Iterable[_T]]:
690
708
  """Check if an object is a iterable of tuple or string mappings."""
691
709
  return isinstance(obj, Iterable) and all(map(make_isinstance(cls), obj))
692
710
 
@@ -730,7 +748,9 @@ def is_sequence_of(
730
748
  def is_sequence_of(
731
749
  obj: Any, cls: tuple[type[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
732
750
  ) -> TypeGuard[Sequence[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
733
- def is_sequence_of(obj: Any, cls: Any, /) -> TypeGuard[Sequence[Any]]:
751
+ @overload
752
+ def is_sequence_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Sequence[_T]]: ...
753
+ def is_sequence_of(obj: Any, cls: TypeLike[_T], /) -> TypeGuard[Sequence[_T]]:
734
754
  """Check if an object is a sequence of tuple or string mappings."""
735
755
  return isinstance(obj, Sequence) and is_iterable_of(obj, cls)
736
756
 
@@ -776,9 +796,23 @@ def is_string_mapping(obj: Any, /) -> TypeGuard[StrMapping]:
776
796
  ##
777
797
 
778
798
 
779
- def is_subclass_int_not_bool(cls: type[Any], /) -> TypeGuard[type[int]]:
780
- """Check if a class is an integer, and not a boolean."""
781
- return issubclass(cls, int) and not issubclass(cls, bool)
799
+ def is_subclass_not_bool_int(cls: type[Any], class_or_tuple: TypeLike[Any], /) -> bool:
800
+ """Check if a subclass relationship holds, except bool<int."""
801
+ match class_or_tuple:
802
+ case type() as parent:
803
+ return _is_subclass_int_not_bool_one(cls, parent)
804
+ case tuple() as parents:
805
+ return any(_is_subclass_int_not_bool_one(cls, p) for p in parents)
806
+ case _ as never:
807
+ assert_never(never)
808
+
809
+
810
+ def _is_subclass_int_not_bool_one(cls: type[Any], parent: type[Any], /) -> bool:
811
+ return issubclass(cls, parent) and not (
812
+ issubclass(cls, bool)
813
+ and issubclass(parent, int)
814
+ and not issubclass(parent, bool)
815
+ )
782
816
 
783
817
 
784
818
  ##
@@ -820,19 +854,14 @@ def make_isinstance(
820
854
  def make_isinstance(
821
855
  cls: tuple[type[_T1], type[_T2], type[_T3], type[_T4], type[_T5]], /
822
856
  ) -> Callable[[Any], TypeGuard[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
823
- def make_isinstance(
824
- cls: type[_T] | tuple[type[_T], ...], /
825
- ) -> Callable[[Any], TypeGuard[Any]]:
857
+ @overload
858
+ def make_isinstance(cls: TypeLike[_T], /) -> Callable[[Any], TypeGuard[_T]]: ...
859
+ def make_isinstance(cls: TypeLike[_T], /) -> Callable[[Any], TypeGuard[_T]]:
826
860
  """Make a curried `isinstance` function."""
827
861
  return partial(_make_instance_core, cls=cls)
828
862
 
829
863
 
830
- ##
831
-
832
-
833
- def _make_instance_core(
834
- obj: Any, /, *, cls: type[_T] | tuple[type[_T], ...]
835
- ) -> TypeGuard[_T]:
864
+ def _make_instance_core(obj: Any, /, *, cls: TypeLike[_T]) -> TypeGuard[_T]:
836
865
  return isinstance(obj, cls)
837
866
 
838
867
 
@@ -1046,7 +1075,7 @@ __all__ = [
1046
1075
  "is_dataclass_class",
1047
1076
  "is_dataclass_instance",
1048
1077
  "is_hashable",
1049
- "is_instance_int_not_bool",
1078
+ "is_instance_not_bool_int",
1050
1079
  "is_iterable_of",
1051
1080
  "is_none",
1052
1081
  "is_not_none",
@@ -1054,7 +1083,7 @@ __all__ = [
1054
1083
  "is_sized",
1055
1084
  "is_sized_not_str",
1056
1085
  "is_string_mapping",
1057
- "is_subclass_int_not_bool",
1086
+ "is_subclass_not_bool_int",
1058
1087
  "is_tuple",
1059
1088
  "is_tuple_or_str_mapping",
1060
1089
  "make_isinstance",
utilities/modules.py CHANGED
@@ -11,6 +11,8 @@ if TYPE_CHECKING:
11
11
  from collections.abc import Callable, Iterable, Iterator
12
12
  from types import ModuleType
13
13
 
14
+ from utilities.types import TypeLike
15
+
14
16
 
15
17
  def is_installed(module: str, /) -> bool:
16
18
  """Check if a module is installed."""
@@ -63,7 +65,7 @@ def yield_module_contents(
63
65
  *,
64
66
  missing_ok: Iterable[str] | None = None,
65
67
  recursive: bool = False,
66
- type: type[Any] | tuple[type[Any], ...] | None = None, # noqa: A002
68
+ type: TypeLike[Any] | None = None, # noqa: A002
67
69
  predicate: Callable[[Any], bool] | None = None,
68
70
  ) -> Iterator[Any]:
69
71
  """Yield all the module contents under a package.
utilities/parse.py CHANGED
@@ -14,7 +14,7 @@ from utilities.datetime import (
14
14
  is_subclass_date_not_datetime,
15
15
  )
16
16
  from utilities.enum import ParseEnumError, parse_enum
17
- from utilities.functions import is_subclass_int_not_bool
17
+ from utilities.functions import is_subclass_not_bool_int
18
18
  from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
19
19
  from utilities.math import ParseNumberError, parse_number
20
20
  from utilities.re import ExtractGroupError, extract_group
@@ -174,7 +174,7 @@ def _parse_object_type(
174
174
  return parse_bool(text)
175
175
  except ParseBoolError:
176
176
  raise _ParseObjectParseError(type_=cls, text=text) from None
177
- if is_subclass_int_not_bool(cls):
177
+ if is_subclass_not_bool_int(cls, int):
178
178
  try:
179
179
  return int(text)
180
180
  except ValueError:
utilities/pyrsistent.py CHANGED
@@ -15,6 +15,8 @@ from pyrsistent._field_common import (
15
15
  if TYPE_CHECKING:
16
16
  from collections.abc import Callable
17
17
 
18
+ from utilities.types import TypeLike
19
+
18
20
 
19
21
  _T = TypeVar("_T")
20
22
  _U = TypeVar("_U")
@@ -62,7 +64,7 @@ def field(
62
64
  ) -> Any: ...
63
65
  def field(
64
66
  *,
65
- type: type[_T] | tuple[type[_T], ...] = PFIELD_NO_TYPE, # noqa: A002
67
+ type: TypeLike[_T] = PFIELD_NO_TYPE, # noqa: A002
66
68
  invariant: Callable[[Any], tuple[bool, Any]] = PFIELD_NO_INVARIANT,
67
69
  default: Any = PFIELD_NO_INITIAL,
68
70
  mandatory: bool = False,
utilities/redis.py CHANGED
@@ -42,7 +42,7 @@ if TYPE_CHECKING:
42
42
  from redis.typing import ResponseT
43
43
 
44
44
  from utilities.iterables import MaybeIterable
45
- from utilities.types import Duration
45
+ from utilities.types import Duration, TypeLike
46
46
 
47
47
 
48
48
  _K = TypeVar("_K")
@@ -69,9 +69,9 @@ class RedisHashMapKey(Generic[_K, _V]):
69
69
  """A hashmap key in a redis store."""
70
70
 
71
71
  name: str
72
- key: type[_K]
72
+ key: TypeLike[_K]
73
73
  key_serializer: Callable[[_K], bytes] | None = None
74
- value: type[_V]
74
+ value: TypeLike[_V]
75
75
  value_serializer: Callable[[_V], bytes] | None = None
76
76
  value_deserializer: Callable[[bytes], _V] | None = None
77
77
  timeout: Duration | None = None
@@ -262,10 +262,24 @@ def redis_hash_map_key(
262
262
  error: type[Exception] = TimeoutError,
263
263
  ttl: Duration | None = None,
264
264
  ) -> RedisHashMapKey[_K1 | _K2 | _K3, _V1 | _V2 | _V3]: ...
265
+ @overload
266
+ def redis_hash_map_key(
267
+ name: str,
268
+ key: TypeLike[_K],
269
+ value: TypeLike[_V],
270
+ /,
271
+ *,
272
+ key_serializer: Callable[[_K1 | _K2 | _K3], bytes] | None = None,
273
+ value_serializer: Callable[[_V1 | _V2 | _V3], bytes] | None = None,
274
+ value_deserializer: Callable[[bytes], _V1 | _V2 | _V3] | None = None,
275
+ timeout: Duration | None = None,
276
+ error: type[Exception] = TimeoutError,
277
+ ttl: Duration | None = None,
278
+ ) -> RedisHashMapKey[_K, _V]: ...
265
279
  def redis_hash_map_key(
266
280
  name: str,
267
- key: Any,
268
- value: Any,
281
+ key: TypeLike[_K],
282
+ value: TypeLike[_V],
269
283
  /,
270
284
  *,
271
285
  key_serializer: Callable[[Any], bytes] | None = None,
@@ -274,7 +288,7 @@ def redis_hash_map_key(
274
288
  timeout: Duration | None = None,
275
289
  ttl: Duration | None = None,
276
290
  error: type[Exception] = TimeoutError,
277
- ) -> RedisHashMapKey[Any, Any]:
291
+ ) -> RedisHashMapKey[_K, _V]:
278
292
  """Create a redis key."""
279
293
  return RedisHashMapKey( # skipif-ci-and-not-linux
280
294
  name=name,
@@ -297,7 +311,7 @@ class RedisKey(Generic[_T]):
297
311
  """A key in a redis store."""
298
312
 
299
313
  name: str
300
- type: type[_T]
314
+ type: TypeLike[_T]
301
315
  serializer: Callable[[_T], bytes] | None = None
302
316
  deserializer: Callable[[bytes], _T] | None = None
303
317
  timeout: Duration | None = None
@@ -420,9 +434,21 @@ def redis_key(
420
434
  error: type[Exception] = TimeoutError,
421
435
  ttl: Duration | None = None,
422
436
  ) -> RedisKey[_T1 | _T2 | _T3 | _T4 | _T5]: ...
437
+ @overload
438
+ def redis_key(
439
+ name: str,
440
+ type_: TypeLike[_T],
441
+ /,
442
+ *,
443
+ serializer: Callable[[_T1 | _T2 | _T3 | _T4 | _T5], bytes] | None = None,
444
+ deserializer: Callable[[bytes], _T1 | _T2 | _T3 | _T4 | _T5] | None = None,
445
+ timeout: Duration | None = None,
446
+ error: type[Exception] = TimeoutError,
447
+ ttl: Duration | None = None,
448
+ ) -> RedisKey[_T]: ...
423
449
  def redis_key(
424
450
  name: str,
425
- type_: Any,
451
+ type_: TypeLike[_T],
426
452
  /,
427
453
  *,
428
454
  serializer: Callable[[Any], bytes] | None = None,
@@ -430,7 +456,7 @@ def redis_key(
430
456
  timeout: Duration | None = None,
431
457
  error: type[Exception] = TimeoutError,
432
458
  ttl: Duration | None = None,
433
- ) -> RedisKey[Any]:
459
+ ) -> RedisKey[_T]:
434
460
  """Create a redis key."""
435
461
  return RedisKey( # skipif-ci-and-not-linux
436
462
  name=name,
utilities/types.py CHANGED
@@ -45,12 +45,13 @@ type OpenMode = Literal[
45
45
  "x+b",
46
46
  "a+b",
47
47
  ]
48
- type StrMapping = Mapping[str, Any]
49
- type StrStrMapping = Mapping[str, str]
50
- type TupleOrStrMapping = tuple[Any, ...] | StrMapping
51
48
  type MaybeCallable[_T] = _T | Callable[[], _T]
52
49
  type MaybeStr[_T] = _T | str
53
50
  type MaybeType[_T] = _T | type[_T]
51
+ type StrMapping = Mapping[str, Any]
52
+ type StrStrMapping = Mapping[str, str]
53
+ type TypeLike[_T] = type[_T] | tuple[type[_T], ...]
54
+ type TupleOrStrMapping = tuple[Any, ...] | StrMapping
54
55
 
55
56
 
56
57
  # asyncio
@@ -327,5 +328,6 @@ __all__ = [
327
328
  "TimeZone",
328
329
  "TimeZoneLike",
329
330
  "TupleOrStrMapping",
331
+ "TypeLike",
330
332
  "WeekDay",
331
333
  ]
utilities/warnings.py CHANGED
@@ -7,12 +7,12 @@ from warnings import catch_warnings, filterwarnings
7
7
  if TYPE_CHECKING:
8
8
  from collections.abc import Iterator
9
9
 
10
+ from utilities.types import TypeLike
11
+
10
12
 
11
13
  @contextmanager
12
14
  def catch_warnings_as_errors(
13
- *,
14
- message: str = "",
15
- category: type[Warning] | tuple[type[Warning], ...] | None = None,
15
+ *, message: str = "", category: TypeLike[Warning] | None = None
16
16
  ) -> Iterator[None]:
17
17
  """Catch warnings as errors."""
18
18
  with _handle_warnings("error", message=message, category=category):
@@ -21,9 +21,7 @@ def catch_warnings_as_errors(
21
21
 
22
22
  @contextmanager
23
23
  def suppress_warnings(
24
- *,
25
- message: str = "",
26
- category: type[Warning] | tuple[type[Warning], ...] | None = None,
24
+ *, message: str = "", category: TypeLike[Warning] | None = None
27
25
  ) -> Iterator[None]:
28
26
  """Suppress warnings."""
29
27
  with _handle_warnings("ignore", message=message, category=category):
@@ -38,7 +36,7 @@ def _handle_warnings(
38
36
  /,
39
37
  *,
40
38
  message: str = "",
41
- category: type[Warning] | tuple[type[Warning], ...] | None = None,
39
+ category: TypeLike[Warning] | None = None,
42
40
  ) -> ExitStack:
43
41
  """Handle a set of warnings."""
44
42
  stack = ExitStack()