dycw-utilities 0.108.2__py3-none-any.whl → 0.108.3__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.108.2.dist-info → dycw_utilities-0.108.3.dist-info}/METADATA +1 -1
- {dycw_utilities-0.108.2.dist-info → dycw_utilities-0.108.3.dist-info}/RECORD +7 -7
- utilities/__init__.py +1 -1
- utilities/functions.py +28 -0
- utilities/python_dotenv.py +16 -159
- {dycw_utilities-0.108.2.dist-info → dycw_utilities-0.108.3.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.108.2.dist-info → dycw_utilities-0.108.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=932kugeJpmqtWXsJGSf5NBQ-6ig0_Xf-TGDJSK9b28w,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
|
@@ -18,7 +18,7 @@ utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
|
|
18
18
|
utilities/eventkit.py,sha256=bG2rjgQqPaaOEW879Pc8vOCX6zRAl1frIhB1Y6fqQXg,13149
|
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=
|
21
|
+
utilities/functions.py,sha256=BH4F_X34tqHuk-BzG9lzooYIP1OmVKm6GQw51qqYShM,27461
|
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
|
@@ -53,7 +53,7 @@ utilities/pyinstrument.py,sha256=ROq2txPwbe2ZUuYJ2IDNbfT97lu2ca0v5_C_yn6sSlM,800
|
|
53
53
|
utilities/pyrsistent.py,sha256=TLJfiiKO4cKNU_pCoM3zDqmSM421qpuoaeaBNnyC_Ac,2489
|
54
54
|
utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
|
55
55
|
utilities/pytest_regressions.py,sha256=Kp1NS_cyXvBFqyiF_oSzYmSJzIOdAZ0SFcSGmbL_UtI,5001
|
56
|
-
utilities/python_dotenv.py,sha256=
|
56
|
+
utilities/python_dotenv.py,sha256=7N4ZbBxXpPNttOTfg-hpaLFFWA5iJwF7tREBzUnbPOM,3415
|
57
57
|
utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
|
58
58
|
utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
|
59
59
|
utilities/redis.py,sha256=CsDQqc9V6ASLzLQwtbQXZQEndyG9pJiCOhPlPeszt7Y,21203
|
@@ -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.108.
|
88
|
-
dycw_utilities-0.108.
|
89
|
-
dycw_utilities-0.108.
|
90
|
-
dycw_utilities-0.108.
|
87
|
+
dycw_utilities-0.108.3.dist-info/METADATA,sha256=A5j6OCjBswwlY0SpdfckG4fb3_nZCjlgIDluLCVAdVY,13004
|
88
|
+
dycw_utilities-0.108.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
89
|
+
dycw_utilities-0.108.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
90
|
+
dycw_utilities-0.108.3.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/functions.py
CHANGED
@@ -5,6 +5,7 @@ from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
5
5
|
from dataclasses import asdict, dataclass, is_dataclass
|
6
6
|
from functools import _lru_cache_wrapper, cached_property, partial, reduce, wraps
|
7
7
|
from inspect import getattr_static
|
8
|
+
from pathlib import Path
|
8
9
|
from re import findall
|
9
10
|
from types import (
|
10
11
|
BuiltinFunctionType,
|
@@ -405,6 +406,31 @@ class EnsureNumberError(Exception):
|
|
405
406
|
##
|
406
407
|
|
407
408
|
|
409
|
+
@overload
|
410
|
+
def ensure_path(obj: Any, /, *, nullable: bool) -> Path | None: ...
|
411
|
+
@overload
|
412
|
+
def ensure_path(obj: Any, /, *, nullable: Literal[False] = False) -> Path: ...
|
413
|
+
def ensure_path(obj: Any, /, *, nullable: bool = False) -> Path | None:
|
414
|
+
"""Ensure an object is a Path."""
|
415
|
+
try:
|
416
|
+
return ensure_class(obj, Path, nullable=nullable)
|
417
|
+
except EnsureClassError as error:
|
418
|
+
raise EnsurePathError(obj=error.obj, nullable=nullable) from None
|
419
|
+
|
420
|
+
|
421
|
+
@dataclass(kw_only=True, slots=True)
|
422
|
+
class EnsurePathError(Exception):
|
423
|
+
obj: Any
|
424
|
+
nullable: bool
|
425
|
+
|
426
|
+
@override
|
427
|
+
def __str__(self) -> str:
|
428
|
+
return _make_error_msg(self.obj, "a Path", nullable=self.nullable)
|
429
|
+
|
430
|
+
|
431
|
+
##
|
432
|
+
|
433
|
+
|
408
434
|
def ensure_sized(obj: Any, /) -> Sized:
|
409
435
|
"""Ensure an object is sized."""
|
410
436
|
if is_sized(obj):
|
@@ -985,6 +1011,7 @@ __all__ = [
|
|
985
1011
|
"EnsureMemberError",
|
986
1012
|
"EnsureNotNoneError",
|
987
1013
|
"EnsureNumberError",
|
1014
|
+
"EnsurePathError",
|
988
1015
|
"EnsureSizedError",
|
989
1016
|
"EnsureSizedNotStrError",
|
990
1017
|
"EnsureStrError",
|
@@ -1004,6 +1031,7 @@ __all__ = [
|
|
1004
1031
|
"ensure_member",
|
1005
1032
|
"ensure_not_none",
|
1006
1033
|
"ensure_number",
|
1034
|
+
"ensure_path",
|
1007
1035
|
"ensure_sized",
|
1008
1036
|
"ensure_sized_not_str",
|
1009
1037
|
"ensure_str",
|
utilities/python_dotenv.py
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import datetime as dt
|
4
3
|
from dataclasses import dataclass
|
5
|
-
from enum import Enum
|
6
4
|
from functools import partial
|
7
5
|
from os import environ
|
8
|
-
from pathlib import Path
|
9
|
-
from re import IGNORECASE, search
|
10
6
|
from typing import TYPE_CHECKING, Any, override
|
11
7
|
|
12
8
|
from dotenv import dotenv_values
|
@@ -16,16 +12,15 @@ from utilities.dataclasses import (
|
|
16
12
|
_YieldFieldsClass,
|
17
13
|
mapping_to_dataclass,
|
18
14
|
)
|
19
|
-
from utilities.enum import EnsureEnumError, ensure_enum
|
20
|
-
from utilities.functions import get_class_name
|
21
15
|
from utilities.git import get_repo_root
|
22
|
-
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
16
|
+
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
17
|
+
from utilities.parse import ParseTextError, parse_text
|
23
18
|
from utilities.pathlib import PWD
|
24
19
|
from utilities.reprlib import get_repr
|
25
|
-
from utilities.typing import get_args, is_literal_type, is_optional_type
|
26
20
|
|
27
21
|
if TYPE_CHECKING:
|
28
22
|
from collections.abc import Mapping
|
23
|
+
from pathlib import Path
|
29
24
|
|
30
25
|
from utilities.types import PathLike, StrMapping, TDataclass
|
31
26
|
|
@@ -65,72 +60,14 @@ def load_settings(
|
|
65
60
|
|
66
61
|
|
67
62
|
def _load_settings_post(
|
68
|
-
field: _YieldFieldsClass[Any],
|
63
|
+
field: _YieldFieldsClass[Any], text: str, /, *, path: Path, values: StrMapping
|
69
64
|
) -> Any:
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
if value == "1" or search("true", value, flags=IGNORECASE):
|
77
|
-
return True
|
78
|
-
raise _LoadSettingsInvalidBoolError(
|
79
|
-
path=path, values=values, field=field.name, value=value
|
80
|
-
)
|
81
|
-
if type_ is float:
|
82
|
-
try:
|
83
|
-
return float(value)
|
84
|
-
except ValueError:
|
85
|
-
raise _LoadSettingsInvalidFloatError(
|
86
|
-
path=path, values=values, field=field.name, value=value
|
87
|
-
) from None
|
88
|
-
if type_ is int:
|
89
|
-
try:
|
90
|
-
return int(value)
|
91
|
-
except ValueError:
|
92
|
-
raise _LoadSettingsInvalidIntError(
|
93
|
-
path=path, values=values, field=field.name, value=value
|
94
|
-
) from None
|
95
|
-
if type_ is Path:
|
96
|
-
return Path(value).expanduser()
|
97
|
-
if type_ is dt.date:
|
98
|
-
from utilities.whenever import ParseDateError, parse_date
|
99
|
-
|
100
|
-
try:
|
101
|
-
return parse_date(value)
|
102
|
-
except ParseDateError:
|
103
|
-
raise _LoadSettingsInvalidDateError(
|
104
|
-
path=path, values=values, field=field.name, value=value
|
105
|
-
) from None
|
106
|
-
if type_ is dt.timedelta:
|
107
|
-
from utilities.whenever import ParseTimedeltaError, parse_timedelta
|
108
|
-
|
109
|
-
try:
|
110
|
-
return parse_timedelta(value)
|
111
|
-
except ParseTimedeltaError:
|
112
|
-
raise _LoadSettingsInvalidTimeDeltaError(
|
113
|
-
path=path, values=values, field=field.name, value=value
|
114
|
-
) from None
|
115
|
-
if isinstance(type_, type) and issubclass(type_, Enum):
|
116
|
-
try:
|
117
|
-
return ensure_enum(value, type_)
|
118
|
-
except EnsureEnumError:
|
119
|
-
raise _LoadSettingsInvalidEnumError(
|
120
|
-
path=path, values=values, field=field.name, type_=type_, value=value
|
121
|
-
) from None
|
122
|
-
if is_literal_type(type_):
|
123
|
-
return one_str(get_args(type_), value)
|
124
|
-
if is_optional_type(type_) and (one(get_args(type_)) is int):
|
125
|
-
if (value is None) or (value == "") or search("none", value, flags=IGNORECASE):
|
126
|
-
return None
|
127
|
-
try:
|
128
|
-
return int(value)
|
129
|
-
except ValueError:
|
130
|
-
raise _LoadSettingsInvalidNullableIntError(
|
131
|
-
path=path, values=values, field=field.name, value=value
|
132
|
-
) from None
|
133
|
-
raise _LoadSettingsTypeError(path=path, field=field.name, type=type_)
|
65
|
+
try:
|
66
|
+
return parse_text(field.type_, text)
|
67
|
+
except ParseTextError:
|
68
|
+
raise _LoadSettingsParseTextError(
|
69
|
+
path=path, values=values, field=field, text=text
|
70
|
+
) from None
|
134
71
|
|
135
72
|
|
136
73
|
@dataclass(kw_only=True, slots=True)
|
@@ -138,13 +75,6 @@ class LoadSettingsError(Exception):
|
|
138
75
|
path: Path
|
139
76
|
|
140
77
|
|
141
|
-
@dataclass(kw_only=True, slots=True)
|
142
|
-
class _LoadSettingsFileNotFoundError(LoadSettingsError):
|
143
|
-
@override
|
144
|
-
def __str__(self) -> str:
|
145
|
-
return f"Path {str(self.path)!r} must exist"
|
146
|
-
|
147
|
-
|
148
78
|
@dataclass(kw_only=True, slots=True)
|
149
79
|
class _LoadSettingsDuplicateKeysError(LoadSettingsError):
|
150
80
|
values: StrMapping
|
@@ -166,94 +96,21 @@ class _LoadSettingsEmptyError(LoadSettingsError):
|
|
166
96
|
|
167
97
|
|
168
98
|
@dataclass(kw_only=True, slots=True)
|
169
|
-
class
|
170
|
-
values: StrMapping
|
171
|
-
field: str
|
172
|
-
value: str
|
173
|
-
|
174
|
-
@override
|
175
|
-
def __str__(self) -> str:
|
176
|
-
return f"Field {self.field!r} must contain a valid boolean; got {self.value!r}"
|
177
|
-
|
178
|
-
|
179
|
-
@dataclass(kw_only=True, slots=True)
|
180
|
-
class _LoadSettingsInvalidDateError(LoadSettingsError):
|
181
|
-
values: StrMapping
|
182
|
-
field: str
|
183
|
-
value: str
|
184
|
-
|
185
|
-
@override
|
186
|
-
def __str__(self) -> str:
|
187
|
-
return f"Field {self.field!r} must contain a valid date; got {self.value!r}"
|
188
|
-
|
189
|
-
|
190
|
-
@dataclass(kw_only=True, slots=True)
|
191
|
-
class _LoadSettingsInvalidEnumError(LoadSettingsError):
|
192
|
-
values: StrMapping
|
193
|
-
field: str
|
194
|
-
type_: type[Enum]
|
195
|
-
value: str
|
196
|
-
|
197
|
-
@override
|
198
|
-
def __str__(self) -> str:
|
199
|
-
type_ = get_class_name(self.type_)
|
200
|
-
return f"Field {self.field!r} must contain a valid member of {type_!r}; got {self.value!r}"
|
201
|
-
|
202
|
-
|
203
|
-
@dataclass(kw_only=True, slots=True)
|
204
|
-
class _LoadSettingsInvalidFloatError(LoadSettingsError):
|
205
|
-
values: StrMapping
|
206
|
-
field: str
|
207
|
-
value: str
|
208
|
-
|
209
|
-
@override
|
210
|
-
def __str__(self) -> str:
|
211
|
-
return f"Field {self.field!r} must contain a valid float; got {self.value!r}"
|
212
|
-
|
213
|
-
|
214
|
-
@dataclass(kw_only=True, slots=True)
|
215
|
-
class _LoadSettingsInvalidIntError(LoadSettingsError):
|
216
|
-
values: StrMapping
|
217
|
-
field: str
|
218
|
-
value: str
|
219
|
-
|
220
|
-
@override
|
221
|
-
def __str__(self) -> str:
|
222
|
-
return f"Field {self.field!r} must contain a valid integer; got {self.value!r}"
|
223
|
-
|
224
|
-
|
225
|
-
@dataclass(kw_only=True, slots=True)
|
226
|
-
class _LoadSettingsInvalidNullableIntError(LoadSettingsError):
|
227
|
-
values: StrMapping
|
228
|
-
field: str
|
229
|
-
value: str
|
230
|
-
|
99
|
+
class _LoadSettingsFileNotFoundError(LoadSettingsError):
|
231
100
|
@override
|
232
101
|
def __str__(self) -> str:
|
233
|
-
return f"
|
102
|
+
return f"Path {str(self.path)!r} must exist"
|
234
103
|
|
235
104
|
|
236
105
|
@dataclass(kw_only=True, slots=True)
|
237
|
-
class
|
106
|
+
class _LoadSettingsParseTextError(LoadSettingsError):
|
238
107
|
values: StrMapping
|
239
|
-
field:
|
240
|
-
|
241
|
-
|
242
|
-
@override
|
243
|
-
def __str__(self) -> str:
|
244
|
-
return (
|
245
|
-
f"Field {self.field!r} must contain a valid timedelta; got {self.value!r}"
|
246
|
-
)
|
247
|
-
|
248
|
-
|
249
|
-
@dataclass(kw_only=True, slots=True)
|
250
|
-
class _LoadSettingsTypeError(LoadSettingsError):
|
251
|
-
field: str
|
252
|
-
type: Any
|
108
|
+
field: _YieldFieldsClass[Any]
|
109
|
+
text: str
|
253
110
|
|
254
111
|
@override
|
255
112
|
def __str__(self) -> str:
|
256
|
-
return f"
|
113
|
+
return f"Unable to parse field {self.field.name!r} of type {self.field.type_!r}; got {self.text!r}"
|
257
114
|
|
258
115
|
|
259
116
|
__all__ = ["LoadSettingsError", "load_settings"]
|
File without changes
|
File without changes
|