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.
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.133.7.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 +31 -51
- 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.7.dist-info → dycw_utilities-0.134.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/licenses/LICENSE +0 -0
utilities/dataclasses.py
CHANGED
@@ -3,16 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from collections.abc import Mapping
|
4
4
|
from contextlib import suppress
|
5
5
|
from dataclasses import MISSING, dataclass, field, fields, replace
|
6
|
-
from typing import
|
7
|
-
TYPE_CHECKING,
|
8
|
-
Any,
|
9
|
-
Generic,
|
10
|
-
Literal,
|
11
|
-
TypeVar,
|
12
|
-
assert_never,
|
13
|
-
overload,
|
14
|
-
override,
|
15
|
-
)
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, assert_never, overload, override
|
16
7
|
|
17
8
|
from utilities.errors import ImpossibleCaseError
|
18
9
|
from utilities.functions import (
|
@@ -43,30 +34,23 @@ from utilities.text import (
|
|
43
34
|
_SplitKeyValuePairsSplitError,
|
44
35
|
split_key_value_pairs,
|
45
36
|
)
|
46
|
-
from utilities.types import
|
47
|
-
ParseObjectExtra,
|
48
|
-
SerializeObjectExtra,
|
49
|
-
StrStrMapping,
|
50
|
-
TDataclass,
|
51
|
-
TSupportsLT,
|
52
|
-
)
|
37
|
+
from utilities.types import SupportsLT
|
53
38
|
from utilities.typing import get_type_hints
|
54
39
|
|
55
40
|
if TYPE_CHECKING:
|
56
41
|
from collections.abc import Callable, Iterable, Iterator
|
57
42
|
from collections.abc import Set as AbstractSet
|
58
43
|
|
59
|
-
from utilities.types import
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
##
|
44
|
+
from utilities.types import (
|
45
|
+
Dataclass,
|
46
|
+
ParseObjectExtra,
|
47
|
+
SerializeObjectExtra,
|
48
|
+
StrMapping,
|
49
|
+
StrStrMapping,
|
50
|
+
)
|
67
51
|
|
68
52
|
|
69
|
-
def dataclass_repr(
|
53
|
+
def dataclass_repr[T](
|
70
54
|
obj: Dataclass,
|
71
55
|
/,
|
72
56
|
*,
|
@@ -77,7 +61,7 @@ def dataclass_repr(
|
|
77
61
|
exclude: Iterable[str] | None = None,
|
78
62
|
rel_tol: float | None = None,
|
79
63
|
abs_tol: float | None = None,
|
80
|
-
extra: Mapping[type[
|
64
|
+
extra: Mapping[type[T], Callable[[T, T], bool]] | None = None,
|
81
65
|
defaults: bool = False,
|
82
66
|
recursive: bool = False,
|
83
67
|
) -> str:
|
@@ -145,7 +129,7 @@ def dataclass_repr(
|
|
145
129
|
##
|
146
130
|
|
147
131
|
|
148
|
-
def dataclass_to_dict(
|
132
|
+
def dataclass_to_dict[T](
|
149
133
|
obj: Dataclass,
|
150
134
|
/,
|
151
135
|
*,
|
@@ -156,7 +140,7 @@ def dataclass_to_dict(
|
|
156
140
|
exclude: Iterable[str] | None = None,
|
157
141
|
rel_tol: float | None = None,
|
158
142
|
abs_tol: float | None = None,
|
159
|
-
extra: Mapping[type[
|
143
|
+
extra: Mapping[type[T], Callable[[T, T], bool]] | None = None,
|
160
144
|
defaults: bool = False,
|
161
145
|
final: Callable[[type[Dataclass], StrMapping], StrMapping] | None = None,
|
162
146
|
recursive: bool = False,
|
@@ -221,7 +205,7 @@ def dataclass_to_dict(
|
|
221
205
|
##
|
222
206
|
|
223
207
|
|
224
|
-
def is_nullable_lt(x:
|
208
|
+
def is_nullable_lt[T: SupportsLT](x: T | None, y: T | None, /) -> bool | None:
|
225
209
|
"""Compare two nullable fields."""
|
226
210
|
match cmp_nullable(x, y):
|
227
211
|
case 1:
|
@@ -237,8 +221,8 @@ def is_nullable_lt(x: TSupportsLT | None, y: TSupportsLT | None, /) -> bool | No
|
|
237
221
|
##
|
238
222
|
|
239
223
|
|
240
|
-
def mapping_to_dataclass(
|
241
|
-
cls: type[
|
224
|
+
def mapping_to_dataclass[T: Dataclass](
|
225
|
+
cls: type[T],
|
242
226
|
mapping: StrMapping,
|
243
227
|
/,
|
244
228
|
*,
|
@@ -249,7 +233,7 @@ def mapping_to_dataclass(
|
|
249
233
|
head: bool = False,
|
250
234
|
case_sensitive: bool = False,
|
251
235
|
allow_extra: bool = False,
|
252
|
-
) ->
|
236
|
+
) -> T:
|
253
237
|
"""Construct a dataclass from a mapping."""
|
254
238
|
if fields is None:
|
255
239
|
fields_use = list(
|
@@ -302,12 +286,12 @@ def mapping_to_dataclass(
|
|
302
286
|
|
303
287
|
|
304
288
|
@dataclass(kw_only=True, slots=True)
|
305
|
-
class MappingToDataclassError(Exception
|
306
|
-
cls: type[
|
289
|
+
class MappingToDataclassError[T: Dataclass](Exception):
|
290
|
+
cls: type[T]
|
307
291
|
|
308
292
|
|
309
293
|
@dataclass(kw_only=True, slots=True)
|
310
|
-
class _MappingToDataClassEmptyError(MappingToDataclassError
|
294
|
+
class _MappingToDataClassEmptyError(MappingToDataclassError):
|
311
295
|
key: str
|
312
296
|
head: bool = False
|
313
297
|
case_sensitive: bool = False
|
@@ -320,7 +304,7 @@ class _MappingToDataClassEmptyError(MappingToDataclassError[TDataclass]):
|
|
320
304
|
|
321
305
|
|
322
306
|
@dataclass(kw_only=True, slots=True)
|
323
|
-
class _MappingToDataClassNonUniqueError(MappingToDataclassError
|
307
|
+
class _MappingToDataClassNonUniqueError(MappingToDataclassError):
|
324
308
|
key: str
|
325
309
|
head: bool = False
|
326
310
|
case_sensitive: bool = False
|
@@ -340,7 +324,7 @@ class _MappingToDataClassNonUniqueError(MappingToDataclassError[TDataclass]):
|
|
340
324
|
|
341
325
|
|
342
326
|
@dataclass(kw_only=True, slots=True)
|
343
|
-
class _MappingToDataClassMissingValuesError(MappingToDataclassError
|
327
|
+
class _MappingToDataClassMissingValuesError(MappingToDataclassError):
|
344
328
|
fields: AbstractSet[str]
|
345
329
|
|
346
330
|
@override
|
@@ -396,15 +380,15 @@ def one_field(
|
|
396
380
|
|
397
381
|
|
398
382
|
@dataclass(kw_only=True, slots=True)
|
399
|
-
class OneFieldError(Exception
|
400
|
-
cls: type[
|
383
|
+
class OneFieldError[T: Dataclass](Exception):
|
384
|
+
cls: type[T]
|
401
385
|
key: str
|
402
386
|
head: bool = False
|
403
387
|
case_sensitive: bool = False
|
404
388
|
|
405
389
|
|
406
390
|
@dataclass(kw_only=True, slots=True)
|
407
|
-
class _OneFieldEmptyError(OneFieldError
|
391
|
+
class _OneFieldEmptyError(OneFieldError):
|
408
392
|
@override
|
409
393
|
def __str__(self) -> str:
|
410
394
|
return _empty_error_str(
|
@@ -413,7 +397,7 @@ class _OneFieldEmptyError(OneFieldError[TDataclass]):
|
|
413
397
|
|
414
398
|
|
415
399
|
@dataclass(kw_only=True, slots=True)
|
416
|
-
class _OneFieldNonUniqueError(OneFieldError
|
400
|
+
class _OneFieldNonUniqueError(OneFieldError):
|
417
401
|
first: str
|
418
402
|
second: str
|
419
403
|
|
@@ -434,19 +418,19 @@ class _OneFieldNonUniqueError(OneFieldError[TDataclass]):
|
|
434
418
|
|
435
419
|
@overload
|
436
420
|
def replace_non_sentinel(
|
437
|
-
obj:
|
421
|
+
obj: Dataclass, /, *, in_place: Literal[True], **kwargs: Any
|
438
422
|
) -> None: ...
|
439
423
|
@overload
|
440
|
-
def replace_non_sentinel(
|
441
|
-
obj:
|
442
|
-
) ->
|
424
|
+
def replace_non_sentinel[T: Dataclass](
|
425
|
+
obj: T, /, *, in_place: Literal[False] = False, **kwargs: Any
|
426
|
+
) -> T: ...
|
443
427
|
@overload
|
444
|
-
def replace_non_sentinel(
|
445
|
-
obj:
|
446
|
-
) ->
|
447
|
-
def replace_non_sentinel(
|
448
|
-
obj:
|
449
|
-
) ->
|
428
|
+
def replace_non_sentinel[T: Dataclass](
|
429
|
+
obj: T, /, *, in_place: bool = False, **kwargs: Any
|
430
|
+
) -> T | None: ...
|
431
|
+
def replace_non_sentinel[T: Dataclass](
|
432
|
+
obj: T, /, *, in_place: bool = False, **kwargs: Any
|
433
|
+
) -> T | None:
|
450
434
|
"""Replace attributes on a dataclass, filtering out sentinel values."""
|
451
435
|
if in_place:
|
452
436
|
for k, v in kwargs.items():
|
@@ -461,7 +445,7 @@ def replace_non_sentinel(
|
|
461
445
|
##
|
462
446
|
|
463
447
|
|
464
|
-
def serialize_dataclass(
|
448
|
+
def serialize_dataclass[T](
|
465
449
|
obj: Dataclass,
|
466
450
|
/,
|
467
451
|
*,
|
@@ -472,7 +456,7 @@ def serialize_dataclass(
|
|
472
456
|
exclude: Iterable[str] | None = None,
|
473
457
|
rel_tol: float | None = None,
|
474
458
|
abs_tol: float | None = None,
|
475
|
-
extra_equal: Mapping[type[
|
459
|
+
extra_equal: Mapping[type[T], Callable[[T, T], bool]] | None = None,
|
476
460
|
defaults: bool = False,
|
477
461
|
list_separator: str = LIST_SEPARATOR,
|
478
462
|
pair_separator: str = PAIR_SEPARATOR,
|
@@ -508,9 +492,9 @@ def serialize_dataclass(
|
|
508
492
|
)
|
509
493
|
|
510
494
|
|
511
|
-
def parse_dataclass(
|
495
|
+
def parse_dataclass[T: Dataclass](
|
512
496
|
text_or_mapping: str | StrStrMapping,
|
513
|
-
cls: type[
|
497
|
+
cls: type[T],
|
514
498
|
/,
|
515
499
|
*,
|
516
500
|
list_separator: str = LIST_SEPARATOR,
|
@@ -523,7 +507,7 @@ def parse_dataclass(
|
|
523
507
|
case_sensitive: bool = False,
|
524
508
|
allow_extra_keys: bool = False,
|
525
509
|
extra_parsers: ParseObjectExtra | None = None,
|
526
|
-
) ->
|
510
|
+
) -> T:
|
527
511
|
"""Construct a dataclass from a string or a mapping or strings."""
|
528
512
|
match text_or_mapping:
|
529
513
|
case str() as text:
|
@@ -597,9 +581,9 @@ def parse_dataclass(
|
|
597
581
|
raise _ParseDataClassMissingValuesError(cls=cls, fields=error.fields) from None
|
598
582
|
|
599
583
|
|
600
|
-
def _parse_dataclass_split_key_value_pairs(
|
584
|
+
def _parse_dataclass_split_key_value_pairs[T: Dataclass](
|
601
585
|
text: str,
|
602
|
-
cls: type[
|
586
|
+
cls: type[T],
|
603
587
|
/,
|
604
588
|
*,
|
605
589
|
list_separator: str = LIST_SEPARATOR,
|
@@ -657,12 +641,12 @@ def _parse_dataclass_parse_text(
|
|
657
641
|
|
658
642
|
|
659
643
|
@dataclass(kw_only=True, slots=True)
|
660
|
-
class ParseDataClassError(Exception
|
661
|
-
cls: type[
|
644
|
+
class ParseDataClassError[T: Dataclass](Exception):
|
645
|
+
cls: type[T]
|
662
646
|
|
663
647
|
|
664
648
|
@dataclass(kw_only=True, slots=True)
|
665
|
-
class _ParseDataClassSplitKeyValuePairsSplitError(ParseDataClassError
|
649
|
+
class _ParseDataClassSplitKeyValuePairsSplitError(ParseDataClassError):
|
666
650
|
text: str
|
667
651
|
|
668
652
|
@override
|
@@ -671,9 +655,7 @@ class _ParseDataClassSplitKeyValuePairsSplitError(ParseDataClassError[TDataclass
|
|
671
655
|
|
672
656
|
|
673
657
|
@dataclass(kw_only=True, slots=True)
|
674
|
-
class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(
|
675
|
-
ParseDataClassError[TDataclass]
|
676
|
-
):
|
658
|
+
class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(ParseDataClassError):
|
677
659
|
counts: Mapping[str, int]
|
678
660
|
|
679
661
|
@override
|
@@ -682,7 +664,7 @@ class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(
|
|
682
664
|
|
683
665
|
|
684
666
|
@dataclass(kw_only=True, slots=True)
|
685
|
-
class _ParseDataClassTextParseError(ParseDataClassError
|
667
|
+
class _ParseDataClassTextParseError(ParseDataClassError):
|
686
668
|
field: _YieldFieldsClass[Any]
|
687
669
|
text: str
|
688
670
|
|
@@ -692,7 +674,7 @@ class _ParseDataClassTextParseError(ParseDataClassError[TDataclass]):
|
|
692
674
|
|
693
675
|
|
694
676
|
@dataclass(kw_only=True, slots=True)
|
695
|
-
class _ParseDataClassTextExtraNonUniqueError(ParseDataClassError
|
677
|
+
class _ParseDataClassTextExtraNonUniqueError(ParseDataClassError):
|
696
678
|
field: _YieldFieldsClass[Any]
|
697
679
|
first: type[Any]
|
698
680
|
second: type[Any]
|
@@ -703,9 +685,7 @@ class _ParseDataClassTextExtraNonUniqueError(ParseDataClassError[TDataclass]):
|
|
703
685
|
|
704
686
|
|
705
687
|
@dataclass(kw_only=True, slots=True)
|
706
|
-
class _ParseDataClassStrMappingToFieldMappingEmptyError(
|
707
|
-
ParseDataClassError[TDataclass]
|
708
|
-
):
|
688
|
+
class _ParseDataClassStrMappingToFieldMappingEmptyError(ParseDataClassError):
|
709
689
|
key: str
|
710
690
|
head: bool = False
|
711
691
|
case_sensitive: bool = False
|
@@ -720,9 +700,7 @@ class _ParseDataClassStrMappingToFieldMappingEmptyError(
|
|
720
700
|
|
721
701
|
|
722
702
|
@dataclass(kw_only=True, slots=True)
|
723
|
-
class _ParseDataClassStrMappingToFieldMappingNonUniqueError(
|
724
|
-
ParseDataClassError[TDataclass]
|
725
|
-
):
|
703
|
+
class _ParseDataClassStrMappingToFieldMappingNonUniqueError(ParseDataClassError):
|
726
704
|
key: str
|
727
705
|
head: bool = False
|
728
706
|
case_sensitive: bool = False
|
@@ -743,7 +721,7 @@ class _ParseDataClassStrMappingToFieldMappingNonUniqueError(
|
|
743
721
|
|
744
722
|
|
745
723
|
@dataclass(kw_only=True, slots=True)
|
746
|
-
class _ParseDataClassMissingValuesError(ParseDataClassError
|
724
|
+
class _ParseDataClassMissingValuesError(ParseDataClassError):
|
747
725
|
fields: AbstractSet[str]
|
748
726
|
|
749
727
|
@override
|
@@ -755,9 +733,9 @@ class _ParseDataClassMissingValuesError(ParseDataClassError[TDataclass]):
|
|
755
733
|
##
|
756
734
|
|
757
735
|
|
758
|
-
def str_mapping_to_field_mapping(
|
759
|
-
cls: type[
|
760
|
-
mapping: Mapping[str,
|
736
|
+
def str_mapping_to_field_mapping[T: Dataclass, U](
|
737
|
+
cls: type[T],
|
738
|
+
mapping: Mapping[str, U],
|
761
739
|
/,
|
762
740
|
*,
|
763
741
|
fields: Iterable[_YieldFieldsClass[Any]] | None = None,
|
@@ -767,7 +745,7 @@ def str_mapping_to_field_mapping(
|
|
767
745
|
head: bool = False,
|
768
746
|
case_sensitive: bool = False,
|
769
747
|
allow_extra: bool = False,
|
770
|
-
) -> Mapping[_YieldFieldsClass[Any],
|
748
|
+
) -> Mapping[_YieldFieldsClass[Any], U]:
|
771
749
|
"""Convert a string-mapping into a field-mapping."""
|
772
750
|
keys_to_fields: Mapping[str, _YieldFieldsClass[Any]] = {}
|
773
751
|
for key in mapping:
|
@@ -800,8 +778,8 @@ def str_mapping_to_field_mapping(
|
|
800
778
|
|
801
779
|
|
802
780
|
@dataclass(kw_only=True, slots=True)
|
803
|
-
class StrMappingToFieldMappingError(Exception
|
804
|
-
cls: type[
|
781
|
+
class StrMappingToFieldMappingError[T: Dataclass](Exception):
|
782
|
+
cls: type[T]
|
805
783
|
key: str
|
806
784
|
head: bool = False
|
807
785
|
case_sensitive: bool = False
|
@@ -913,12 +891,12 @@ def yield_fields(
|
|
913
891
|
|
914
892
|
|
915
893
|
@dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
|
916
|
-
class _YieldFieldsInstance
|
894
|
+
class _YieldFieldsInstance[T]:
|
917
895
|
name: str
|
918
|
-
value:
|
896
|
+
value: T = field(hash=False)
|
919
897
|
type_: Any = field(hash=False)
|
920
|
-
default:
|
921
|
-
default_factory: Callable[[],
|
898
|
+
default: T | Sentinel = field(default=sentinel, hash=False)
|
899
|
+
default_factory: Callable[[], T] | Sentinel = field(default=sentinel, hash=False)
|
922
900
|
repr: bool = True
|
923
901
|
hash_: bool | None = None
|
924
902
|
init: bool = True
|
@@ -926,12 +904,12 @@ class _YieldFieldsInstance(Generic[_T]):
|
|
926
904
|
metadata: StrMapping = field(default_factory=dict, hash=False)
|
927
905
|
kw_only: bool | Sentinel = sentinel
|
928
906
|
|
929
|
-
def equals_default(
|
907
|
+
def equals_default[U](
|
930
908
|
self,
|
931
909
|
*,
|
932
910
|
rel_tol: float | None = None,
|
933
911
|
abs_tol: float | None = None,
|
934
|
-
extra: Mapping[type[
|
912
|
+
extra: Mapping[type[U], Callable[[U, U], bool]] | None = None,
|
935
913
|
) -> bool:
|
936
914
|
"""Check if the field value equals its default."""
|
937
915
|
if isinstance(self.default, Sentinel) and isinstance(
|
@@ -954,14 +932,14 @@ class _YieldFieldsInstance(Generic[_T]):
|
|
954
932
|
self.value, expected, rel_tol=rel_tol, abs_tol=abs_tol, extra=extra
|
955
933
|
)
|
956
934
|
|
957
|
-
def keep(
|
935
|
+
def keep[U](
|
958
936
|
self,
|
959
937
|
*,
|
960
938
|
include: Iterable[str] | None = None,
|
961
939
|
exclude: Iterable[str] | None = None,
|
962
940
|
rel_tol: float | None = None,
|
963
941
|
abs_tol: float | None = None,
|
964
|
-
extra: Mapping[type[
|
942
|
+
extra: Mapping[type[U], Callable[[U, U], bool]] | None = None,
|
965
943
|
defaults: bool = False,
|
966
944
|
) -> bool:
|
967
945
|
"""Whether to include a field."""
|
@@ -974,11 +952,11 @@ class _YieldFieldsInstance(Generic[_T]):
|
|
974
952
|
|
975
953
|
|
976
954
|
@dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
|
977
|
-
class _YieldFieldsClass
|
955
|
+
class _YieldFieldsClass[T]:
|
978
956
|
name: str
|
979
957
|
type_: Any = field(hash=False)
|
980
|
-
default:
|
981
|
-
default_factory: Callable[[],
|
958
|
+
default: T | Sentinel = field(default=sentinel, hash=False)
|
959
|
+
default_factory: Callable[[], T] | Sentinel = field(default=sentinel, hash=False)
|
982
960
|
repr: bool = True
|
983
961
|
hash_: bool | None = None
|
984
962
|
init: bool = True
|
utilities/enum.py
CHANGED
@@ -2,26 +2,29 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from enum import Enum, StrEnum
|
5
|
-
from typing import
|
5
|
+
from typing import TYPE_CHECKING, Literal, assert_never, overload, override
|
6
6
|
|
7
7
|
from utilities.functions import ensure_str
|
8
8
|
from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
|
9
|
-
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from utilities.types import EnumLike
|
12
|
+
|
10
13
|
|
11
14
|
##
|
12
15
|
|
13
16
|
|
14
17
|
@overload
|
15
|
-
def ensure_enum(
|
16
|
-
value: None, enum: type[
|
18
|
+
def ensure_enum[E: Enum](
|
19
|
+
value: None, enum: type[E], /, *, case_sensitive: bool = False
|
17
20
|
) -> None: ...
|
18
21
|
@overload
|
19
|
-
def ensure_enum(
|
20
|
-
value: EnumLike[
|
21
|
-
) ->
|
22
|
-
def ensure_enum(
|
23
|
-
value: EnumLike[
|
24
|
-
) ->
|
22
|
+
def ensure_enum[E: Enum](
|
23
|
+
value: EnumLike[E], enum: type[E], /, *, case_sensitive: bool = False
|
24
|
+
) -> E: ...
|
25
|
+
def ensure_enum[E: Enum](
|
26
|
+
value: EnumLike[E] | None, enum: type[E], /, *, case_sensitive: bool = False
|
27
|
+
) -> E | None:
|
25
28
|
"""Ensure the object is a member of the enum."""
|
26
29
|
if value is None:
|
27
30
|
return None
|
@@ -36,9 +39,9 @@ def ensure_enum(
|
|
36
39
|
|
37
40
|
|
38
41
|
@dataclass(kw_only=True, slots=True)
|
39
|
-
class EnsureEnumError(Exception
|
40
|
-
value: EnumLike[
|
41
|
-
enum: type[
|
42
|
+
class EnsureEnumError[E: Enum](Exception):
|
43
|
+
value: EnumLike[E]
|
44
|
+
enum: type[E]
|
42
45
|
|
43
46
|
|
44
47
|
@dataclass(kw_only=True, slots=True)
|
@@ -58,9 +61,9 @@ class _EnsureEnumParseError(EnsureEnumError):
|
|
58
61
|
##
|
59
62
|
|
60
63
|
|
61
|
-
def parse_enum(
|
62
|
-
value: str, enum: type[
|
63
|
-
) ->
|
64
|
+
def parse_enum[E: Enum](
|
65
|
+
value: str, enum: type[E], /, *, case_sensitive: bool = False
|
66
|
+
) -> E:
|
64
67
|
"""Parse a string into the enum."""
|
65
68
|
by_name = _parse_enum_one(value, enum, "names", case_sensitive=case_sensitive)
|
66
69
|
if not issubclass(enum, StrEnum):
|
@@ -99,14 +102,14 @@ def parse_enum(
|
|
99
102
|
type _NamesOrValues = Literal["names", "values"]
|
100
103
|
|
101
104
|
|
102
|
-
def _parse_enum_one(
|
105
|
+
def _parse_enum_one[E: Enum](
|
103
106
|
value: str,
|
104
|
-
enum: type[
|
107
|
+
enum: type[E],
|
105
108
|
names_or_values: _NamesOrValues,
|
106
109
|
/,
|
107
110
|
*,
|
108
111
|
case_sensitive: bool = False,
|
109
|
-
) ->
|
112
|
+
) -> E | None:
|
110
113
|
"""Pair one aspect of the enums."""
|
111
114
|
match names_or_values:
|
112
115
|
case "names":
|
@@ -132,9 +135,9 @@ def _parse_enum_one(
|
|
132
135
|
|
133
136
|
|
134
137
|
@dataclass(kw_only=True, slots=True)
|
135
|
-
class ParseEnumError(Exception
|
138
|
+
class ParseEnumError[E: Enum](Exception):
|
136
139
|
value: str
|
137
|
-
enum: type[
|
140
|
+
enum: type[E]
|
138
141
|
|
139
142
|
|
140
143
|
@dataclass(kw_only=True, slots=True)
|