dycw-utilities 0.109.3__py3-none-any.whl → 0.109.4__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.109.3
3
+ Version: 0.109.4
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=XHS4BHXBA86Rjy07zPtJwGLkB2QC4JUa0pQjTRZS2zE,60
1
+ utilities/__init__.py,sha256=TfQi7UYijWAmuZ8wsQ7kQKbgWiSQWgaXE80fBUn8qx8,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,7 +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=Wj5pzG_VdgoAy14a7Luhem-BgYrRtRFvvl_POiszRd0,36930
42
42
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
43
- utilities/parse.py,sha256=-rnx9qBKo9UZbE1HtvBwYOs4vF7YiJiciPAM6IKmfRc,5183
43
+ utilities/parse.py,sha256=q1A1-bsXltdAh8We5Acy-lPuVIClWTxsRXQWq5wYyKk,6382
44
44
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
45
45
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
46
46
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
@@ -84,7 +84,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
84
84
  utilities/whenever.py,sha256=5x2t47VJmJRWcd_NLFy54NkB3uom-XQYxEbLtEfL1bs,17775
85
85
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
86
86
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
87
- dycw_utilities-0.109.3.dist-info/METADATA,sha256=TF66WIpVFJnaJA80lxa_GKBZKZUzfz8VWd95FQW8v4A,13004
88
- dycw_utilities-0.109.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
- dycw_utilities-0.109.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
- dycw_utilities-0.109.3.dist-info/RECORD,,
87
+ dycw_utilities-0.109.4.dist-info/METADATA,sha256=-fU_tSw6cCv59Fg6s0I0pUlV3YzXeuq15kfirsKUmik,13004
88
+ dycw_utilities-0.109.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
+ dycw_utilities-0.109.4.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
+ dycw_utilities-0.109.4.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.3"
3
+ __version__ = "0.109.4"
utilities/parse.py CHANGED
@@ -7,30 +7,42 @@ from enum import Enum
7
7
  from pathlib import Path
8
8
  from re import DOTALL
9
9
  from types import NoneType
10
- from typing import Any, override
10
+ from typing import TYPE_CHECKING, Any, TypeVar, override
11
11
 
12
12
  from utilities.datetime import is_subclass_date_not_datetime
13
13
  from utilities.enum import ParseEnumError, parse_enum
14
14
  from utilities.functions import is_subclass_int_not_bool
15
- from utilities.iterables import one, one_str
15
+ from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
16
16
  from utilities.re import ExtractGroupError, extract_group
17
17
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
18
18
  from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
19
19
  from utilities.typing import get_args, is_literal_type, is_optional_type, is_tuple_type
20
20
  from utilities.version import ParseVersionError, Version, parse_version
21
21
 
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Callable, Mapping
24
+
25
+
26
+ _T = TypeVar("_T")
27
+
22
28
 
23
29
  def parse_text(
24
- obj: Any, text: str, /, *, case_sensitive: bool = False, head: bool = False
30
+ obj: Any,
31
+ text: str,
32
+ /,
33
+ *,
34
+ case_sensitive: bool = False,
35
+ head: bool = False,
36
+ extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
25
37
  ) -> Any:
26
38
  """Parse text."""
27
39
  if obj is None:
28
40
  try:
29
41
  return parse_none(text)
30
42
  except ParseNoneError:
31
- raise ParseTextError(obj=obj, text=text) from None
43
+ raise _ParseTextParseError(obj=obj, text=text) from None
32
44
  if isinstance(obj, type):
33
- return _parse_text_type(obj, text, case_sensitive=case_sensitive)
45
+ return _parse_text_type(obj, text, case_sensitive=case_sensitive, extra=extra)
34
46
  if is_literal_type(obj):
35
47
  return one_str(get_args(obj), text, head=head, case_sensitive=case_sensitive)
36
48
  if is_optional_type(obj):
@@ -42,95 +54,111 @@ def parse_text(
42
54
  ):
43
55
  try:
44
56
  return _parse_text_type(inner, text, case_sensitive=case_sensitive)
45
- except ParseTextError:
46
- raise ParseTextError(obj=obj, text=text) from None
57
+ except _ParseTextParseError:
58
+ raise _ParseTextParseError(obj=obj, text=text) from None
47
59
  if is_tuple_type(obj):
48
60
  args = get_args(obj)
49
61
  try:
50
62
  texts = extract_group(r"^\((.*)\)$", text, flags=DOTALL).split(", ")
51
63
  except ExtractGroupError:
52
- raise ParseTextError(obj=obj, text=text) from None
64
+ raise _ParseTextParseError(obj=obj, text=text) from None
53
65
  if len(args) != len(texts):
54
- raise ParseTextError(obj=obj, text=text)
66
+ raise _ParseTextParseError(obj=obj, text=text)
55
67
  return tuple(
56
68
  parse_text(arg, text, case_sensitive=case_sensitive, head=head)
57
69
  for arg, text in zip(args, texts, strict=True)
58
70
  )
59
- raise ParseTextError(obj=obj, text=text) from None
71
+ raise _ParseTextParseError(obj=obj, text=text) from None
60
72
 
61
73
 
62
74
  def _parse_text_type(
63
- cls: type[Any], text: str, /, *, case_sensitive: bool = False
75
+ cls: type[Any],
76
+ text: str,
77
+ /,
78
+ *,
79
+ case_sensitive: bool = False,
80
+ extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
64
81
  ) -> Any:
65
82
  """Parse text."""
66
83
  if issubclass(cls, NoneType):
67
84
  try:
68
85
  return parse_none(text)
69
86
  except ParseNoneError:
70
- raise ParseTextError(obj=cls, text=text) from None
87
+ raise _ParseTextParseError(obj=cls, text=text) from None
71
88
  if issubclass(cls, str):
72
89
  return text
73
90
  if issubclass(cls, bool):
74
91
  try:
75
92
  return parse_bool(text)
76
93
  except ParseBoolError:
77
- raise ParseTextError(obj=cls, text=text) from None
94
+ raise _ParseTextParseError(obj=cls, text=text) from None
78
95
  if is_subclass_int_not_bool(cls):
79
96
  try:
80
97
  return int(text)
81
98
  except ValueError:
82
- raise ParseTextError(obj=cls, text=text) from None
99
+ raise _ParseTextParseError(obj=cls, text=text) from None
83
100
  if issubclass(cls, float):
84
101
  try:
85
102
  return float(text)
86
103
  except ValueError:
87
- raise ParseTextError(obj=cls, text=text) from None
104
+ raise _ParseTextParseError(obj=cls, text=text) from None
88
105
  if issubclass(cls, Enum):
89
106
  try:
90
107
  return parse_enum(text, cls, case_sensitive=case_sensitive)
91
108
  except ParseEnumError:
92
- raise ParseTextError(obj=cls, text=text) from None
109
+ raise _ParseTextParseError(obj=cls, text=text) from None
93
110
  if issubclass(cls, Path):
94
111
  return Path(text).expanduser()
95
112
  if issubclass(cls, Sentinel):
96
113
  try:
97
114
  return parse_sentinel(text)
98
115
  except ParseSentinelError:
99
- raise ParseTextError(obj=cls, text=text) from None
116
+ raise _ParseTextParseError(obj=cls, text=text) from None
100
117
  if issubclass(cls, Version):
101
118
  try:
102
119
  return parse_version(text)
103
120
  except ParseVersionError:
104
- raise ParseTextError(obj=cls, text=text) from None
121
+ raise _ParseTextParseError(obj=cls, text=text) from None
105
122
  if is_subclass_date_not_datetime(cls):
106
123
  from utilities.whenever import ParseDateError, parse_date
107
124
 
108
125
  try:
109
126
  return parse_date(text)
110
127
  except ParseDateError:
111
- raise ParseTextError(obj=cls, text=text) from None
128
+ raise _ParseTextParseError(obj=cls, text=text) from None
112
129
  if issubclass(cls, dt.datetime):
113
130
  from utilities.whenever import ParseDateTimeError, parse_datetime
114
131
 
115
132
  try:
116
133
  return parse_datetime(text)
117
134
  except ParseDateTimeError:
118
- raise ParseTextError(obj=cls, text=text) from None
135
+ raise _ParseTextParseError(obj=cls, text=text) from None
119
136
  if issubclass(cls, dt.time):
120
137
  from utilities.whenever import ParseTimeError, parse_time
121
138
 
122
139
  try:
123
140
  return parse_time(text)
124
141
  except ParseTimeError:
125
- raise ParseTextError(obj=cls, text=text) from None
142
+ raise _ParseTextParseError(obj=cls, text=text) from None
126
143
  if issubclass(cls, dt.timedelta):
127
144
  from utilities.whenever import ParseTimedeltaError, parse_timedelta
128
145
 
129
146
  try:
130
147
  return parse_timedelta(text)
131
148
  except ParseTimedeltaError:
132
- raise ParseTextError(obj=cls, text=text) from None
133
- raise ParseTextError(obj=cls, text=text) from None
149
+ raise _ParseTextParseError(obj=cls, text=text) from None
150
+ if extra is not None:
151
+ try:
152
+ parser = one(p for c, p in extra.items() if issubclass(cls, c))
153
+ except OneEmptyError:
154
+ pass
155
+ except OneNonUniqueError as error:
156
+ raise _ParseTextExtraNonUniqueError(
157
+ obj=cls, text=text, first=error.first, second=error.second
158
+ ) from None
159
+ else:
160
+ return parser(text)
161
+ raise _ParseTextParseError(obj=cls, text=text) from None
134
162
 
135
163
 
136
164
  @dataclass
@@ -138,6 +166,19 @@ class ParseTextError(Exception):
138
166
  obj: Any
139
167
  text: str
140
168
 
169
+
170
+ @dataclass
171
+ class _ParseTextParseError(ParseTextError):
141
172
  @override
142
173
  def __str__(self) -> str:
143
174
  return f"Unable to parse {self.obj!r}; got {self.text!r}"
175
+
176
+
177
+ @dataclass
178
+ class _ParseTextExtraNonUniqueError(ParseTextError):
179
+ first: type[Any]
180
+ second: type[Any]
181
+
182
+ @override
183
+ def __str__(self) -> str:
184
+ return f"Unable to parse {self.obj!r} since `extra` must contain exactly one parent class; got {self.first!r}, {self.second!r} and perhaps more"