dycw-utilities 0.110.2__py3-none-any.whl → 0.110.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.
- {dycw_utilities-0.110.2.dist-info → dycw_utilities-0.110.4.dist-info}/METADATA +1 -1
- {dycw_utilities-0.110.2.dist-info → dycw_utilities-0.110.4.dist-info}/RECORD +9 -9
- utilities/__init__.py +1 -1
- utilities/dataclasses.py +42 -17
- utilities/parse.py +62 -9
- utilities/text.py +48 -21
- utilities/types.py +2 -0
- {dycw_utilities-0.110.2.dist-info → dycw_utilities-0.110.4.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.110.2.dist-info → dycw_utilities-0.110.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=jGUTgOEiXSlv_bOz8eQW5saCU2Pq4oTqEXt6FeWlFII,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
|
@@ -11,7 +11,7 @@ utilities/contextlib.py,sha256=OOIIEa5lXKGzFAnauaul40nlQnQko6Na4ryiMJcHkIg,478
|
|
11
11
|
utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
12
12
|
utilities/cryptography.py,sha256=HyOewI20cl3uRXsKivhIaeLVDInQdzgXZGaly7hS5dE,771
|
13
13
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
14
|
-
utilities/dataclasses.py,sha256=
|
14
|
+
utilities/dataclasses.py,sha256=Q197PVnE_vUMn_SNnqJBCo4eRy4bdHtgMHWRbSJPtFk,26670
|
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
|
@@ -41,7 +41,7 @@ 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=
|
44
|
+
utilities/parse.py,sha256=6erE2TpZP-z0FWtxnnDVO3-X1d1MHoNrr2IYiaJEuk8,18960
|
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
|
@@ -73,11 +73,11 @@ utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
|
|
73
73
|
utilities/sys.py,sha256=h0Xr7Vj86wNalvwJVP1wj5Y0kD_VWm1vzuXZ_jw94mE,2743
|
74
74
|
utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
|
75
75
|
utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
|
76
|
-
utilities/text.py,sha256=
|
76
|
+
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=
|
80
|
+
utilities/types.py,sha256=PZjxTCDYSPFKppi3ZWnSh441mv0rgEvZkh8npqL6awo,18064
|
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
|
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
|
|
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.
|
91
|
-
dycw_utilities-0.110.
|
92
|
-
dycw_utilities-0.110.
|
93
|
-
dycw_utilities-0.110.
|
90
|
+
dycw_utilities-0.110.4.dist-info/METADATA,sha256=N7v2g5hRFwzAbpEjcnZs0OZQhPZPECwQHsevYVzsXZg,13004
|
91
|
+
dycw_utilities-0.110.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
92
|
+
dycw_utilities-0.110.4.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
93
|
+
dycw_utilities-0.110.4.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/dataclasses.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from collections.abc import Mapping
|
4
|
+
from contextlib import suppress
|
4
5
|
from dataclasses import MISSING, dataclass, field, fields, replace
|
5
6
|
from typing import (
|
6
7
|
TYPE_CHECKING,
|
@@ -21,21 +22,23 @@ from utilities.functions import (
|
|
21
22
|
)
|
22
23
|
from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
|
23
24
|
from utilities.operator import is_equal
|
24
|
-
from utilities.parse import
|
25
|
-
|
26
|
-
PAIR_SEPARATOR,
|
27
|
-
ParseObjectError,
|
28
|
-
parse_object,
|
29
|
-
serialize_object,
|
30
|
-
)
|
25
|
+
from utilities.parse import ParseObjectError, parse_object, serialize_object
|
26
|
+
from utilities.re import ExtractGroupError, extract_group
|
31
27
|
from utilities.sentinel import Sentinel, sentinel
|
32
28
|
from utilities.text import (
|
29
|
+
BRACKETS,
|
30
|
+
LIST_SEPARATOR,
|
31
|
+
PAIR_SEPARATOR,
|
33
32
|
_SplitKeyValuePairsDuplicateKeysError,
|
34
33
|
_SplitKeyValuePairsSplitError,
|
35
|
-
join_strs,
|
36
34
|
split_key_value_pairs,
|
37
35
|
)
|
38
|
-
from utilities.types import
|
36
|
+
from utilities.types import (
|
37
|
+
ParseObjectExtra,
|
38
|
+
SerializeObjectExtra,
|
39
|
+
StrStrMapping,
|
40
|
+
TDataclass,
|
41
|
+
)
|
39
42
|
from utilities.typing import get_type_hints
|
40
43
|
|
41
44
|
if TYPE_CHECKING:
|
@@ -408,10 +411,11 @@ def serialize_dataclass(
|
|
408
411
|
exclude: Iterable[str] | None = None,
|
409
412
|
rel_tol: float | None = None,
|
410
413
|
abs_tol: float | None = None,
|
411
|
-
|
414
|
+
extra_equal: Mapping[type[_U], Callable[[_U, _U], bool]] | None = None,
|
412
415
|
defaults: bool = False,
|
413
416
|
list_separator: str = LIST_SEPARATOR,
|
414
417
|
pair_separator: str = PAIR_SEPARATOR,
|
418
|
+
extra_serializers: SerializeObjectExtra | None = None,
|
415
419
|
) -> str:
|
416
420
|
"""Serialize a Dataclass."""
|
417
421
|
mapping: StrStrMapping = {}
|
@@ -426,16 +430,21 @@ def serialize_dataclass(
|
|
426
430
|
exclude=exclude,
|
427
431
|
rel_tol=rel_tol,
|
428
432
|
abs_tol=abs_tol,
|
429
|
-
extra=
|
433
|
+
extra=extra_equal,
|
430
434
|
defaults=defaults,
|
431
435
|
):
|
432
436
|
mapping[fld.name] = serialize_object(
|
433
|
-
fld.value,
|
437
|
+
fld.value,
|
438
|
+
list_separator=list_separator,
|
439
|
+
pair_separator=pair_separator,
|
440
|
+
extra=extra_serializers,
|
434
441
|
)
|
435
|
-
|
436
|
-
|
442
|
+
return serialize_object(
|
443
|
+
mapping,
|
444
|
+
list_separator=list_separator,
|
445
|
+
pair_separator=pair_separator,
|
446
|
+
extra=extra_serializers,
|
437
447
|
)
|
438
|
-
return join_strs(joined_items, separator=list_separator)
|
439
448
|
|
440
449
|
|
441
450
|
def parse_dataclass(
|
@@ -445,6 +454,7 @@ def parse_dataclass(
|
|
445
454
|
*,
|
446
455
|
list_separator: str = LIST_SEPARATOR,
|
447
456
|
pair_separator: str = PAIR_SEPARATOR,
|
457
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
448
458
|
globalns: StrMapping | None = None,
|
449
459
|
localns: StrMapping | None = None,
|
450
460
|
warn_name_errors: bool = False,
|
@@ -457,7 +467,11 @@ def parse_dataclass(
|
|
457
467
|
match text_or_mapping:
|
458
468
|
case str() as text:
|
459
469
|
keys_to_serializes = _parse_dataclass_split_key_value_pairs(
|
460
|
-
text,
|
470
|
+
text,
|
471
|
+
cls,
|
472
|
+
list_separator=list_separator,
|
473
|
+
pair_separator=pair_separator,
|
474
|
+
brackets=brackets,
|
461
475
|
)
|
462
476
|
case Mapping() as keys_to_serializes:
|
463
477
|
...
|
@@ -481,7 +495,14 @@ def parse_dataclass(
|
|
481
495
|
)
|
482
496
|
field_names_to_values = {
|
483
497
|
f.name: _parse_dataclass_parse_text(
|
484
|
-
f,
|
498
|
+
f,
|
499
|
+
t,
|
500
|
+
cls,
|
501
|
+
list_separator=list_separator,
|
502
|
+
pair_separator=pair_separator,
|
503
|
+
head=head,
|
504
|
+
case_sensitive=case_sensitive,
|
505
|
+
extra=extra_parsers,
|
485
506
|
)
|
486
507
|
for f, t in fields_to_serializes.items()
|
487
508
|
}
|
@@ -505,12 +526,16 @@ def _parse_dataclass_split_key_value_pairs(
|
|
505
526
|
*,
|
506
527
|
list_separator: str = LIST_SEPARATOR,
|
507
528
|
pair_separator: str = PAIR_SEPARATOR,
|
529
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
508
530
|
) -> StrStrMapping:
|
531
|
+
with suppress(ExtractGroupError):
|
532
|
+
text = extract_group(r"^\{?(.*?)\}?$", text)
|
509
533
|
try:
|
510
534
|
return split_key_value_pairs(
|
511
535
|
text,
|
512
536
|
list_separator=list_separator,
|
513
537
|
pair_separator=pair_separator,
|
538
|
+
brackets=brackets,
|
514
539
|
mapping=True,
|
515
540
|
)
|
516
541
|
except _SplitKeyValuePairsSplitError as error:
|
utilities/parse.py
CHANGED
@@ -20,6 +20,9 @@ from utilities.math import ParseNumberError, parse_number
|
|
20
20
|
from utilities.re import ExtractGroupError, extract_group
|
21
21
|
from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
|
22
22
|
from utilities.text import (
|
23
|
+
BRACKETS,
|
24
|
+
LIST_SEPARATOR,
|
25
|
+
PAIR_SEPARATOR,
|
23
26
|
ParseBoolError,
|
24
27
|
ParseNoneError,
|
25
28
|
join_strs,
|
@@ -28,7 +31,7 @@ from utilities.text import (
|
|
28
31
|
split_key_value_pairs,
|
29
32
|
split_str,
|
30
33
|
)
|
31
|
-
from utilities.types import Duration, Number, ParseObjectExtra
|
34
|
+
from utilities.types import Duration, Number, ParseObjectExtra, SerializeObjectExtra
|
32
35
|
from utilities.typing import (
|
33
36
|
get_args,
|
34
37
|
is_dict_type,
|
@@ -43,14 +46,10 @@ from utilities.typing import (
|
|
43
46
|
from utilities.version import ParseVersionError, Version, parse_version
|
44
47
|
|
45
48
|
if TYPE_CHECKING:
|
46
|
-
from collections.abc import Mapping, Sequence
|
49
|
+
from collections.abc import Iterable, Mapping, Sequence
|
47
50
|
from collections.abc import Set as AbstractSet
|
48
51
|
|
49
52
|
|
50
|
-
LIST_SEPARATOR = ","
|
51
|
-
PAIR_SEPARATOR = "="
|
52
|
-
|
53
|
-
|
54
53
|
def parse_object(
|
55
54
|
type_: Any,
|
56
55
|
text: str,
|
@@ -58,6 +57,7 @@ def parse_object(
|
|
58
57
|
*,
|
59
58
|
list_separator: str = LIST_SEPARATOR,
|
60
59
|
pair_separator: str = PAIR_SEPARATOR,
|
60
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
61
61
|
head: bool = False,
|
62
62
|
case_sensitive: bool = False,
|
63
63
|
extra: ParseObjectExtra | None = None,
|
@@ -78,6 +78,7 @@ def parse_object(
|
|
78
78
|
text,
|
79
79
|
list_separator=list_separator,
|
80
80
|
pair_separator=pair_separator,
|
81
|
+
brackets=brackets,
|
81
82
|
head=head,
|
82
83
|
case_sensitive=case_sensitive,
|
83
84
|
extra=extra,
|
@@ -89,6 +90,7 @@ def parse_object(
|
|
89
90
|
text,
|
90
91
|
list_separator=list_separator,
|
91
92
|
pair_separator=pair_separator,
|
93
|
+
brackets=brackets,
|
92
94
|
head=head,
|
93
95
|
case_sensitive=case_sensitive,
|
94
96
|
extra=extra,
|
@@ -100,6 +102,7 @@ def parse_object(
|
|
100
102
|
text,
|
101
103
|
list_separator=list_separator,
|
102
104
|
pair_separator=pair_separator,
|
105
|
+
brackets=brackets,
|
103
106
|
head=head,
|
104
107
|
case_sensitive=case_sensitive,
|
105
108
|
extra=extra,
|
@@ -116,6 +119,7 @@ def parse_object(
|
|
116
119
|
text,
|
117
120
|
list_separator=list_separator,
|
118
121
|
pair_separator=pair_separator,
|
122
|
+
brackets=brackets,
|
119
123
|
head=head,
|
120
124
|
case_sensitive=case_sensitive,
|
121
125
|
extra=extra,
|
@@ -128,6 +132,7 @@ def parse_object(
|
|
128
132
|
text,
|
129
133
|
list_separator=list_separator,
|
130
134
|
pair_separator=pair_separator,
|
135
|
+
brackets=brackets,
|
131
136
|
head=head,
|
132
137
|
case_sensitive=case_sensitive,
|
133
138
|
extra=extra,
|
@@ -138,6 +143,7 @@ def parse_object(
|
|
138
143
|
text,
|
139
144
|
list_separator=list_separator,
|
140
145
|
pair_separator=pair_separator,
|
146
|
+
brackets=brackets,
|
141
147
|
head=head,
|
142
148
|
case_sensitive=case_sensitive,
|
143
149
|
extra=extra,
|
@@ -234,7 +240,7 @@ def _parse_object_type(
|
|
234
240
|
) from None
|
235
241
|
else:
|
236
242
|
return parser(text)
|
237
|
-
raise _ParseObjectParseError(type_=cls, text=text)
|
243
|
+
raise _ParseObjectParseError(type_=cls, text=text)
|
238
244
|
|
239
245
|
|
240
246
|
def _parse_object_dict_type(
|
@@ -244,6 +250,7 @@ def _parse_object_dict_type(
|
|
244
250
|
*,
|
245
251
|
list_separator: str = LIST_SEPARATOR,
|
246
252
|
pair_separator: str = PAIR_SEPARATOR,
|
253
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
247
254
|
head: bool = False,
|
248
255
|
case_sensitive: bool = False,
|
249
256
|
extra: ParseObjectExtra | None = None,
|
@@ -257,6 +264,7 @@ def _parse_object_dict_type(
|
|
257
264
|
inner_text,
|
258
265
|
list_separator=list_separator,
|
259
266
|
pair_separator=pair_separator,
|
267
|
+
brackets=brackets,
|
260
268
|
mapping=True,
|
261
269
|
)
|
262
270
|
keys = (
|
@@ -265,6 +273,7 @@ def _parse_object_dict_type(
|
|
265
273
|
k,
|
266
274
|
list_separator=list_separator,
|
267
275
|
pair_separator=pair_separator,
|
276
|
+
brackets=brackets,
|
268
277
|
head=head,
|
269
278
|
case_sensitive=case_sensitive,
|
270
279
|
extra=extra,
|
@@ -277,6 +286,7 @@ def _parse_object_dict_type(
|
|
277
286
|
v,
|
278
287
|
list_separator=list_separator,
|
279
288
|
pair_separator=pair_separator,
|
289
|
+
brackets=brackets,
|
280
290
|
head=head,
|
281
291
|
case_sensitive=case_sensitive,
|
282
292
|
extra=extra,
|
@@ -296,6 +306,7 @@ def _parse_object_list_type(
|
|
296
306
|
*,
|
297
307
|
list_separator: str = LIST_SEPARATOR,
|
298
308
|
pair_separator: str = PAIR_SEPARATOR,
|
309
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
299
310
|
head: bool = False,
|
300
311
|
case_sensitive: bool = False,
|
301
312
|
extra: ParseObjectExtra | None = None,
|
@@ -313,6 +324,7 @@ def _parse_object_list_type(
|
|
313
324
|
t,
|
314
325
|
list_separator=list_separator,
|
315
326
|
pair_separator=pair_separator,
|
327
|
+
brackets=brackets,
|
316
328
|
head=head,
|
317
329
|
case_sensitive=case_sensitive,
|
318
330
|
extra=extra,
|
@@ -330,6 +342,7 @@ def _parse_object_set_type(
|
|
330
342
|
*,
|
331
343
|
list_separator: str = LIST_SEPARATOR,
|
332
344
|
pair_separator: str = PAIR_SEPARATOR,
|
345
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
333
346
|
head: bool = False,
|
334
347
|
case_sensitive: bool = False,
|
335
348
|
extra: ParseObjectExtra | None = None,
|
@@ -347,6 +360,7 @@ def _parse_object_set_type(
|
|
347
360
|
t,
|
348
361
|
list_separator=list_separator,
|
349
362
|
pair_separator=pair_separator,
|
363
|
+
brackets=brackets,
|
350
364
|
head=head,
|
351
365
|
case_sensitive=case_sensitive,
|
352
366
|
extra=extra,
|
@@ -389,6 +403,7 @@ def _parse_object_tuple_type(
|
|
389
403
|
*,
|
390
404
|
list_separator: str = LIST_SEPARATOR,
|
391
405
|
pair_separator: str = PAIR_SEPARATOR,
|
406
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
392
407
|
head: bool = False,
|
393
408
|
case_sensitive: bool = False,
|
394
409
|
extra: ParseObjectExtra | None = None,
|
@@ -408,6 +423,7 @@ def _parse_object_tuple_type(
|
|
408
423
|
text,
|
409
424
|
list_separator=list_separator,
|
410
425
|
pair_separator=pair_separator,
|
426
|
+
brackets=brackets,
|
411
427
|
head=head,
|
412
428
|
case_sensitive=case_sensitive,
|
413
429
|
extra=extra,
|
@@ -450,6 +466,7 @@ def serialize_object(
|
|
450
466
|
*,
|
451
467
|
list_separator: str = LIST_SEPARATOR,
|
452
468
|
pair_separator: str = PAIR_SEPARATOR,
|
469
|
+
extra: SerializeObjectExtra | None = None,
|
453
470
|
) -> str:
|
454
471
|
"""Convert an object to text."""
|
455
472
|
if (obj is None) or isinstance(
|
@@ -490,7 +507,9 @@ def serialize_object(
|
|
490
507
|
return _serialize_object_set(
|
491
508
|
obj, list_separator=list_separator, pair_separator=pair_separator
|
492
509
|
)
|
493
|
-
|
510
|
+
if extra is not None:
|
511
|
+
return _serialize_object_extra(obj, extra)
|
512
|
+
raise _SerializeObjectSerializeError(obj=obj)
|
494
513
|
|
495
514
|
|
496
515
|
def _serialize_object_dict(
|
@@ -518,6 +537,19 @@ def _serialize_object_dict(
|
|
518
537
|
return f"{{{joined}}}"
|
519
538
|
|
520
539
|
|
540
|
+
def _serialize_object_extra(obj: Any, extra: SerializeObjectExtra, /) -> str:
|
541
|
+
try:
|
542
|
+
serializer = one(s for c, s in extra.items() if isinstance(obj, c))
|
543
|
+
except OneEmptyError:
|
544
|
+
raise _SerializeObjectSerializeError(obj=obj) from None
|
545
|
+
except OneNonUniqueError as error:
|
546
|
+
raise _SerializeObjectExtraNonUniqueError(
|
547
|
+
obj=obj, first=error.first, second=error.second
|
548
|
+
) from None
|
549
|
+
else:
|
550
|
+
return serializer(obj)
|
551
|
+
|
552
|
+
|
521
553
|
def _serialize_object_list(
|
522
554
|
obj: Sequence[Any],
|
523
555
|
/,
|
@@ -569,4 +601,25 @@ def _serialize_object_tuple(
|
|
569
601
|
return f"({joined})"
|
570
602
|
|
571
603
|
|
572
|
-
|
604
|
+
@dataclass(kw_only=True, slots=True)
|
605
|
+
class SerializeObjectError(Exception):
|
606
|
+
obj: Any
|
607
|
+
|
608
|
+
|
609
|
+
class _SerializeObjectSerializeError(SerializeObjectError):
|
610
|
+
@override
|
611
|
+
def __str__(self) -> str:
|
612
|
+
return f"Unable to serialize object {self.obj!r}"
|
613
|
+
|
614
|
+
|
615
|
+
@dataclass
|
616
|
+
class _SerializeObjectExtraNonUniqueError(SerializeObjectError):
|
617
|
+
first: type[Any]
|
618
|
+
second: type[Any]
|
619
|
+
|
620
|
+
@override
|
621
|
+
def __str__(self) -> str:
|
622
|
+
return f"Unable to serialize object {self.obj!r} since `extra` must contain exactly one parent class; got {self.first!r}, {self.second!r} and perhaps more"
|
623
|
+
|
624
|
+
|
625
|
+
__all__ = ["parse_object", "serialize_object"]
|
utilities/text.py
CHANGED
@@ -17,6 +17,12 @@ if TYPE_CHECKING:
|
|
17
17
|
from utilities.types import StrStrMapping
|
18
18
|
|
19
19
|
|
20
|
+
DEFAULT_SEPARATOR = ","
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
|
25
|
+
|
20
26
|
def parse_bool(text: str, /) -> bool:
|
21
27
|
"""Parse text into a boolean value."""
|
22
28
|
if text == "0" or search("false", text, flags=IGNORECASE):
|
@@ -88,13 +94,19 @@ def _snake_case_title(match: Match[str], /) -> str:
|
|
88
94
|
##
|
89
95
|
|
90
96
|
|
97
|
+
LIST_SEPARATOR = DEFAULT_SEPARATOR
|
98
|
+
PAIR_SEPARATOR = "="
|
99
|
+
BRACKETS = [("(", ")"), ("[", "]"), ("{", "}")]
|
100
|
+
|
101
|
+
|
91
102
|
@overload
|
92
103
|
def split_key_value_pairs(
|
93
104
|
text: str,
|
94
105
|
/,
|
95
106
|
*,
|
96
|
-
list_separator: str =
|
97
|
-
pair_separator: str =
|
107
|
+
list_separator: str = DEFAULT_SEPARATOR,
|
108
|
+
pair_separator: str = PAIR_SEPARATOR,
|
109
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
98
110
|
mapping: Literal[True],
|
99
111
|
) -> StrStrMapping: ...
|
100
112
|
@overload
|
@@ -102,8 +114,9 @@ def split_key_value_pairs(
|
|
102
114
|
text: str,
|
103
115
|
/,
|
104
116
|
*,
|
105
|
-
list_separator: str =
|
106
|
-
pair_separator: str =
|
117
|
+
list_separator: str = DEFAULT_SEPARATOR,
|
118
|
+
pair_separator: str = PAIR_SEPARATOR,
|
119
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
107
120
|
mapping: Literal[False] = False,
|
108
121
|
) -> Sequence[tuple[str, str]]: ...
|
109
122
|
@overload
|
@@ -111,23 +124,29 @@ def split_key_value_pairs(
|
|
111
124
|
text: str,
|
112
125
|
/,
|
113
126
|
*,
|
114
|
-
list_separator: str =
|
115
|
-
pair_separator: str =
|
127
|
+
list_separator: str = DEFAULT_SEPARATOR,
|
128
|
+
pair_separator: str = PAIR_SEPARATOR,
|
129
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
116
130
|
mapping: bool = False,
|
117
131
|
) -> Sequence[tuple[str, str]] | StrStrMapping: ...
|
118
132
|
def split_key_value_pairs(
|
119
133
|
text: str,
|
120
134
|
/,
|
121
135
|
*,
|
122
|
-
list_separator: str =
|
123
|
-
pair_separator: str =
|
136
|
+
list_separator: str = DEFAULT_SEPARATOR,
|
137
|
+
pair_separator: str = PAIR_SEPARATOR,
|
138
|
+
brackets: Iterable[tuple[str, str]] | None = BRACKETS,
|
124
139
|
mapping: bool = False,
|
125
140
|
) -> Sequence[tuple[str, str]] | StrStrMapping:
|
126
141
|
"""Split a string into key-value pairs."""
|
142
|
+
try:
|
143
|
+
texts = split_str(text, separator=list_separator, brackets=brackets)
|
144
|
+
except SplitStrError as error:
|
145
|
+
raise _SplitKeyValuePairsSplitError(text=text, inner=error.text) from None
|
127
146
|
try:
|
128
147
|
pairs = [
|
129
|
-
split_str(text_i, separator=pair_separator, n=2)
|
130
|
-
for text_i in
|
148
|
+
split_str(text_i, separator=pair_separator, brackets=brackets, n=2)
|
149
|
+
for text_i in texts
|
131
150
|
]
|
132
151
|
except SplitStrError as error:
|
133
152
|
raise _SplitKeyValuePairsSplitError(text=text, inner=error.text) from None
|
@@ -153,7 +172,7 @@ class _SplitKeyValuePairsSplitError(SplitKeyValuePairsError):
|
|
153
172
|
|
154
173
|
@override
|
155
174
|
def __str__(self) -> str:
|
156
|
-
return f"Unable to split {self.text!r} into key-value pairs
|
175
|
+
return f"Unable to split {self.text!r} into key-value pairs"
|
157
176
|
|
158
177
|
|
159
178
|
@dataclass(kw_only=True, slots=True)
|
@@ -173,7 +192,7 @@ def split_str(
|
|
173
192
|
text: str,
|
174
193
|
/,
|
175
194
|
*,
|
176
|
-
separator: str =
|
195
|
+
separator: str = DEFAULT_SEPARATOR,
|
177
196
|
brackets: Iterable[tuple[str, str]] | None = None,
|
178
197
|
n: Literal[1],
|
179
198
|
) -> tuple[str]: ...
|
@@ -182,7 +201,7 @@ def split_str(
|
|
182
201
|
text: str,
|
183
202
|
/,
|
184
203
|
*,
|
185
|
-
separator: str =
|
204
|
+
separator: str = DEFAULT_SEPARATOR,
|
186
205
|
brackets: Iterable[tuple[str, str]] | None = None,
|
187
206
|
n: Literal[2],
|
188
207
|
) -> tuple[str, str]: ...
|
@@ -191,7 +210,7 @@ def split_str(
|
|
191
210
|
text: str,
|
192
211
|
/,
|
193
212
|
*,
|
194
|
-
separator: str =
|
213
|
+
separator: str = DEFAULT_SEPARATOR,
|
195
214
|
brackets: Iterable[tuple[str, str]] | None = None,
|
196
215
|
n: Literal[3],
|
197
216
|
) -> tuple[str, str, str]: ...
|
@@ -200,7 +219,7 @@ def split_str(
|
|
200
219
|
text: str,
|
201
220
|
/,
|
202
221
|
*,
|
203
|
-
separator: str =
|
222
|
+
separator: str = DEFAULT_SEPARATOR,
|
204
223
|
brackets: Iterable[tuple[str, str]] | None = None,
|
205
224
|
n: Literal[4],
|
206
225
|
) -> tuple[str, str, str, str]: ...
|
@@ -209,7 +228,7 @@ def split_str(
|
|
209
228
|
text: str,
|
210
229
|
/,
|
211
230
|
*,
|
212
|
-
separator: str =
|
231
|
+
separator: str = DEFAULT_SEPARATOR,
|
213
232
|
brackets: Iterable[tuple[str, str]] | None = None,
|
214
233
|
n: Literal[5],
|
215
234
|
) -> tuple[str, str, str, str, str]: ...
|
@@ -218,7 +237,7 @@ def split_str(
|
|
218
237
|
text: str,
|
219
238
|
/,
|
220
239
|
*,
|
221
|
-
separator: str =
|
240
|
+
separator: str = DEFAULT_SEPARATOR,
|
222
241
|
brackets: Iterable[tuple[str, str]] | None = None,
|
223
242
|
n: int | None = None,
|
224
243
|
) -> Sequence[str]: ...
|
@@ -226,7 +245,7 @@ def split_str(
|
|
226
245
|
text: str,
|
227
246
|
/,
|
228
247
|
*,
|
229
|
-
separator: str =
|
248
|
+
separator: str = DEFAULT_SEPARATOR,
|
230
249
|
brackets: Iterable[tuple[str, str]] | None = None,
|
231
250
|
n: int | None = None,
|
232
251
|
) -> Sequence[str]:
|
@@ -247,7 +266,11 @@ def split_str(
|
|
247
266
|
|
248
267
|
|
249
268
|
def _split_str_brackets(
|
250
|
-
text: str,
|
269
|
+
text: str,
|
270
|
+
brackets: Iterable[tuple[str, str]],
|
271
|
+
/,
|
272
|
+
*,
|
273
|
+
separator: str = DEFAULT_SEPARATOR,
|
251
274
|
) -> Sequence[str]:
|
252
275
|
brackets = list(brackets)
|
253
276
|
opens, closes = transpose(brackets)
|
@@ -338,7 +361,7 @@ class _SplitStrOpeningBracketUnmatchedError(SplitStrError):
|
|
338
361
|
|
339
362
|
|
340
363
|
def join_strs(
|
341
|
-
texts: Iterable[str], /, *, sort: bool = False, separator: str =
|
364
|
+
texts: Iterable[str], /, *, sort: bool = False, separator: str = DEFAULT_SEPARATOR
|
342
365
|
) -> str:
|
343
366
|
"""Join a collection of strings, with a special provision for the empty list."""
|
344
367
|
texts = list(texts)
|
@@ -351,7 +374,7 @@ def join_strs(
|
|
351
374
|
return separator.join(texts)
|
352
375
|
|
353
376
|
|
354
|
-
def _escape_separator(*, separator: str =
|
377
|
+
def _escape_separator(*, separator: str = DEFAULT_SEPARATOR) -> str:
|
355
378
|
return f"\\{separator}"
|
356
379
|
|
357
380
|
|
@@ -373,6 +396,10 @@ def strip_and_dedent(text: str, /, *, trailing: bool = False) -> str:
|
|
373
396
|
|
374
397
|
|
375
398
|
__all__ = [
|
399
|
+
"BRACKETS",
|
400
|
+
"DEFAULT_SEPARATOR",
|
401
|
+
"LIST_SEPARATOR",
|
402
|
+
"PAIR_SEPARATOR",
|
376
403
|
"ParseBoolError",
|
377
404
|
"ParseNoneError",
|
378
405
|
"SplitKeyValuePairsError",
|
utilities/types.py
CHANGED
@@ -232,6 +232,7 @@ class SupportsRound(Protocol[_T_co]):
|
|
232
232
|
|
233
233
|
# parse
|
234
234
|
type ParseObjectExtra = Mapping[Any, Callable[[str], Any]]
|
235
|
+
type SerializeObjectExtra = Mapping[type[Any], Callable[[Any], str]]
|
235
236
|
|
236
237
|
|
237
238
|
# pathlib
|
@@ -288,6 +289,7 @@ __all__ = [
|
|
288
289
|
"PathLikeOrCallable",
|
289
290
|
"RoundMode",
|
290
291
|
"Seed",
|
292
|
+
"SerializeObjectExtra",
|
291
293
|
"StrMapping",
|
292
294
|
"StrStrMapping",
|
293
295
|
"SupportsAbs",
|
File without changes
|
File without changes
|