dycw-utilities 0.108.1__py3-none-any.whl → 0.108.2__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.108.1
3
+ Version: 0.108.2
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=IeBN4DoVYNosR_DmJpLKbjCsXl79LuQiIIKs3QarfVo,60
1
+ utilities/__init__.py,sha256=cA1_Gx_WFoPXFOqGWzIcljSCQWUSLDE4oIIYB9493-g,60
2
2
  utilities/altair.py,sha256=NSyDsm8QlkAGmsGdxVwCkHnPxt_35yJBa9Lg7bz9Ays,9054
3
3
  utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
4
4
  utilities/asyncio.py,sha256=41oQUurWMvadFK5gFnaG21hMM0Vmfn2WS6OpC0R9mas,14757
@@ -40,6 +40,7 @@ utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
40
40
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
41
41
  utilities/orjson.py,sha256=DW5pOpMyrR5Q8caQYly9AqRPazDBqrWv5GRWfULqka4,36291
42
42
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
43
+ utilities/parse.py,sha256=vKVWIqR5JykQzPSnMHQr7_h43M6TwfYEnPmjmbgSA-o,4585
43
44
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
44
45
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
45
46
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
@@ -59,7 +60,7 @@ utilities/redis.py,sha256=CsDQqc9V6ASLzLQwtbQXZQEndyG9pJiCOhPlPeszt7Y,21203
59
60
  utilities/reprlib.py,sha256=Re9bk3n-kC__9DxQmRlevqFA86pE6TtVfWjUgpbVOv0,1849
60
61
  utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
61
62
  utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
62
- utilities/sentinel.py,sha256=Y_UnVkIMlbN6pJquUz2ghY1rTmCo93k_os3PjyoqE9Q,684
63
+ utilities/sentinel.py,sha256=0X1GWWcnPxGCo7wDgVTOfhU5iKlXdvVZudlMM0iezDw,1246
63
64
  utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
64
65
  utilities/slack_sdk.py,sha256=SeDNMh24IPiEBWoGMdgvrflUaFa9TGlTS03H9-NKaQw,4132
65
66
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
@@ -69,7 +70,7 @@ utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
69
70
  utilities/sys.py,sha256=h0Xr7Vj86wNalvwJVP1wj5Y0kD_VWm1vzuXZ_jw94mE,2743
70
71
  utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
71
72
  utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
72
- utilities/text.py,sha256=4DSi_m0E1Bxqp5LhMiPH_m38ag6wQAGrWP_iPkfBo9Q,2688
73
+ utilities/text.py,sha256=X_EjRQeV_PsG3oP7OiGYIyXGKWqciTnSwoKhM2tsy6M,3120
73
74
  utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
74
75
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
75
76
  utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
@@ -83,7 +84,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
83
84
  utilities/whenever.py,sha256=5x2t47VJmJRWcd_NLFy54NkB3uom-XQYxEbLtEfL1bs,17775
84
85
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
85
86
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
86
- dycw_utilities-0.108.1.dist-info/METADATA,sha256=VOAnUEUC7wcoSrSFoy7zijMLaJYHCt-euTVZdVZThEU,13004
87
- dycw_utilities-0.108.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
88
- dycw_utilities-0.108.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
89
- dycw_utilities-0.108.1.dist-info/RECORD,,
87
+ dycw_utilities-0.108.2.dist-info/METADATA,sha256=N4v4D-Slw7bPRUT5KdnuEIJnUoDQVx3pTRFXkQ8kNp4,13004
88
+ dycw_utilities-0.108.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
+ dycw_utilities-0.108.2.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
+ dycw_utilities-0.108.2.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.108.1"
3
+ __version__ = "0.108.2"
utilities/parse.py ADDED
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime as dt
4
+ from contextlib import suppress
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from types import NoneType
9
+ from typing import Any, get_args, override
10
+
11
+ from utilities.datetime import is_subclass_date_not_datetime
12
+ from utilities.enum import ParseEnumError, parse_enum
13
+ from utilities.functions import is_subclass_int_not_bool
14
+ from utilities.iterables import one, one_str
15
+ from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
16
+ from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
17
+ from utilities.typing import is_literal_type, is_optional_type
18
+ from utilities.version import ParseVersionError, Version, parse_version
19
+
20
+
21
+ def parse_text(
22
+ obj: Any, text: str, /, *, case_sensitive: bool = False, head: bool = False
23
+ ) -> Any:
24
+ """Parse text."""
25
+ if obj is None:
26
+ try:
27
+ return parse_none(text)
28
+ except ParseNoneError:
29
+ raise ParseTextError(obj=obj, text=text) from None
30
+ if isinstance(obj, type):
31
+ return _parse_text_type(obj, text, case_sensitive=case_sensitive)
32
+ if is_literal_type(obj):
33
+ return one_str(get_args(obj), text, head=head, case_sensitive=case_sensitive)
34
+ if is_optional_type(obj):
35
+ with suppress(ParseNoneError):
36
+ return parse_none(text)
37
+ inner = one(arg for arg in get_args(obj) if arg is not NoneType)
38
+ if isinstance(
39
+ inner := one(arg for arg in get_args(obj) if arg is not NoneType), type
40
+ ):
41
+ try:
42
+ return _parse_text_type(inner, text, case_sensitive=case_sensitive)
43
+ except ParseTextError:
44
+ raise ParseTextError(obj=obj, text=text) from None
45
+ raise ParseTextError(obj=obj, text=text) from None
46
+
47
+
48
+ def _parse_text_type(
49
+ cls: type[Any], text: str, /, *, case_sensitive: bool = False
50
+ ) -> Any:
51
+ """Parse text."""
52
+ if issubclass(cls, NoneType):
53
+ try:
54
+ return parse_none(text)
55
+ except ParseNoneError:
56
+ raise ParseTextError(obj=cls, text=text) from None
57
+ if issubclass(cls, str):
58
+ return text
59
+ if issubclass(cls, bool):
60
+ try:
61
+ return parse_bool(text)
62
+ except ParseBoolError:
63
+ raise ParseTextError(obj=cls, text=text) from None
64
+ if is_subclass_int_not_bool(cls):
65
+ try:
66
+ return int(text)
67
+ except ValueError:
68
+ raise ParseTextError(obj=cls, text=text) from None
69
+ if issubclass(cls, float):
70
+ try:
71
+ return float(text)
72
+ except ValueError:
73
+ raise ParseTextError(obj=cls, text=text) from None
74
+ if issubclass(cls, Enum):
75
+ try:
76
+ return parse_enum(text, cls, case_sensitive=case_sensitive)
77
+ except ParseEnumError:
78
+ raise ParseTextError(obj=cls, text=text) from None
79
+ if issubclass(cls, Path):
80
+ return Path(text).expanduser()
81
+ if issubclass(cls, Sentinel):
82
+ try:
83
+ return parse_sentinel(text)
84
+ except ParseSentinelError:
85
+ raise ParseTextError(obj=cls, text=text) from None
86
+ if issubclass(cls, Version):
87
+ try:
88
+ return parse_version(text)
89
+ except ParseVersionError:
90
+ raise ParseTextError(obj=cls, text=text) from None
91
+ if is_subclass_date_not_datetime(cls):
92
+ from utilities.whenever import ParseDateError, parse_date
93
+
94
+ try:
95
+ return parse_date(text)
96
+ except ParseDateError:
97
+ raise ParseTextError(obj=cls, text=text) from None
98
+ if issubclass(cls, dt.datetime):
99
+ from utilities.whenever import ParseDateTimeError, parse_datetime
100
+
101
+ try:
102
+ return parse_datetime(text)
103
+ except ParseDateTimeError:
104
+ raise ParseTextError(obj=cls, text=text) from None
105
+ if issubclass(cls, dt.time):
106
+ from utilities.whenever import ParseTimeError, parse_time
107
+
108
+ try:
109
+ return parse_time(text)
110
+ except ParseTimeError:
111
+ raise ParseTextError(obj=cls, text=text) from None
112
+ if issubclass(cls, dt.timedelta):
113
+ from utilities.whenever import ParseTimedeltaError, parse_timedelta
114
+
115
+ try:
116
+ return parse_timedelta(text)
117
+ except ParseTimedeltaError:
118
+ raise ParseTextError(obj=cls, text=text) from None
119
+ raise ParseTextError(obj=cls, text=text) from None
120
+
121
+
122
+ @dataclass
123
+ class ParseTextError(Exception):
124
+ obj: Any
125
+ text: str
126
+
127
+ @override
128
+ def __str__(self) -> str:
129
+ return f"Unable to parse {self.obj!r}; got {self.text!r}"
utilities/sentinel.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from dataclasses import dataclass
4
+ from re import IGNORECASE, search
3
5
  from typing import Any, override
4
6
 
5
7
 
@@ -33,4 +35,29 @@ class Sentinel(metaclass=_Meta):
33
35
  sentinel = Sentinel()
34
36
 
35
37
 
36
- __all__ = ["SENTINEL_REPR", "Sentinel", "sentinel"]
38
+ ##
39
+
40
+
41
+ def parse_sentinel(text: str, /) -> Sentinel:
42
+ """Parse text into the Sentinel value."""
43
+ if text == "" or search("Sentinel", text, flags=IGNORECASE):
44
+ return sentinel
45
+ raise ParseSentinelError(text=text)
46
+
47
+
48
+ @dataclass(kw_only=True, slots=True)
49
+ class ParseSentinelError(Exception):
50
+ text: str
51
+
52
+ @override
53
+ def __str__(self) -> str:
54
+ return f"Unable to parse sentinel value; got {self.text!r}"
55
+
56
+
57
+ __all__ = [
58
+ "SENTINEL_REPR",
59
+ "ParseSentinelError",
60
+ "Sentinel",
61
+ "parse_sentinel",
62
+ "sentinel",
63
+ ]
utilities/text.py CHANGED
@@ -45,7 +45,26 @@ class ParseBoolError(Exception):
45
45
 
46
46
  @override
47
47
  def __str__(self) -> str:
48
- return f"Unable to parse {self.text!r} into a boolean value"
48
+ return f"Unable to parse boolean value; got {self.text!r}"
49
+
50
+
51
+ ##
52
+
53
+
54
+ def parse_none(text: str, /) -> None:
55
+ """Parse text into the None value."""
56
+ if text == "" or search("None", text, flags=IGNORECASE):
57
+ return
58
+ raise ParseNoneError(text=text)
59
+
60
+
61
+ @dataclass(kw_only=True, slots=True)
62
+ class ParseNoneError(Exception):
63
+ text: str
64
+
65
+ @override
66
+ def __str__(self) -> str:
67
+ return f"Unable to parse null value; got {self.text!r}"
49
68
 
50
69
 
51
70
  ##
@@ -108,8 +127,10 @@ def strip_and_dedent(text: str, /, *, trailing: bool = False) -> str:
108
127
 
109
128
  __all__ = [
110
129
  "ParseBoolError",
130
+ "ParseNoneError",
111
131
  "join_strs",
112
132
  "parse_bool",
133
+ "parse_none",
113
134
  "repr_encode",
114
135
  "snake_case",
115
136
  "split_str",