dycw-utilities 0.110.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.110.3
3
+ Version: 0.110.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=SPilOBQUT3LssunOfCSklrHeCvASfNJWhSufL04nwh8,60
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=pIRUFMD9JDrwudl16w4a242hoM4A6kCQqirUfsYRcL0,25925
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=RRRTu3TOTDWiO-vVjP6VIg5Z-BojN2dRwPHQGxDQCGg,17758
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=0rdjh4_qqGf_wbJyCKQDIbqrb0fQRke2pJEEr_TAWoM,10918
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=Hi9aKaxN3G9zFVlLjx6U9xd_HMGq-eqHLxmG1cSdVpg,17967
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.3.dist-info/METADATA,sha256=0rjOLcXZr0om0AtgTlgw0_4Zh_Lbh8k7ic_EqfmwP5s,13004
91
- dycw_utilities-0.110.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.110.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.110.3.dist-info/RECORD,,
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
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.110.3"
3
+ __version__ = "0.110.4"
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,
@@ -22,16 +23,22 @@ from utilities.functions import (
22
23
  from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
23
24
  from utilities.operator import is_equal
24
25
  from utilities.parse import ParseObjectError, parse_object, serialize_object
26
+ from utilities.re import ExtractGroupError, extract_group
25
27
  from utilities.sentinel import Sentinel, sentinel
26
28
  from utilities.text import (
29
+ BRACKETS,
27
30
  LIST_SEPARATOR,
28
31
  PAIR_SEPARATOR,
29
32
  _SplitKeyValuePairsDuplicateKeysError,
30
33
  _SplitKeyValuePairsSplitError,
31
- join_strs,
32
34
  split_key_value_pairs,
33
35
  )
34
- from utilities.types import ParseObjectExtra, StrStrMapping, TDataclass
36
+ from utilities.types import (
37
+ ParseObjectExtra,
38
+ SerializeObjectExtra,
39
+ StrStrMapping,
40
+ TDataclass,
41
+ )
35
42
  from utilities.typing import get_type_hints
36
43
 
37
44
  if TYPE_CHECKING:
@@ -404,10 +411,11 @@ def serialize_dataclass(
404
411
  exclude: Iterable[str] | None = None,
405
412
  rel_tol: float | None = None,
406
413
  abs_tol: float | None = None,
407
- extra: Mapping[type[_U], Callable[[_U, _U], bool]] | None = None,
414
+ extra_equal: Mapping[type[_U], Callable[[_U, _U], bool]] | None = None,
408
415
  defaults: bool = False,
409
416
  list_separator: str = LIST_SEPARATOR,
410
417
  pair_separator: str = PAIR_SEPARATOR,
418
+ extra_serializers: SerializeObjectExtra | None = None,
411
419
  ) -> str:
412
420
  """Serialize a Dataclass."""
413
421
  mapping: StrStrMapping = {}
@@ -422,16 +430,21 @@ def serialize_dataclass(
422
430
  exclude=exclude,
423
431
  rel_tol=rel_tol,
424
432
  abs_tol=abs_tol,
425
- extra=extra,
433
+ extra=extra_equal,
426
434
  defaults=defaults,
427
435
  ):
428
436
  mapping[fld.name] = serialize_object(
429
- fld.value, list_separator=list_separator, pair_separator=pair_separator
437
+ fld.value,
438
+ list_separator=list_separator,
439
+ pair_separator=pair_separator,
440
+ extra=extra_serializers,
430
441
  )
431
- joined_items = (
432
- join_strs(item, separator=pair_separator) for item in mapping.items()
442
+ return serialize_object(
443
+ mapping,
444
+ list_separator=list_separator,
445
+ pair_separator=pair_separator,
446
+ extra=extra_serializers,
433
447
  )
434
- return join_strs(joined_items, separator=list_separator)
435
448
 
436
449
 
437
450
  def parse_dataclass(
@@ -441,6 +454,7 @@ def parse_dataclass(
441
454
  *,
442
455
  list_separator: str = LIST_SEPARATOR,
443
456
  pair_separator: str = PAIR_SEPARATOR,
457
+ brackets: Iterable[tuple[str, str]] | None = BRACKETS,
444
458
  globalns: StrMapping | None = None,
445
459
  localns: StrMapping | None = None,
446
460
  warn_name_errors: bool = False,
@@ -453,7 +467,11 @@ def parse_dataclass(
453
467
  match text_or_mapping:
454
468
  case str() as text:
455
469
  keys_to_serializes = _parse_dataclass_split_key_value_pairs(
456
- text, cls, list_separator=list_separator, pair_separator=pair_separator
470
+ text,
471
+ cls,
472
+ list_separator=list_separator,
473
+ pair_separator=pair_separator,
474
+ brackets=brackets,
457
475
  )
458
476
  case Mapping() as keys_to_serializes:
459
477
  ...
@@ -477,7 +495,14 @@ def parse_dataclass(
477
495
  )
478
496
  field_names_to_values = {
479
497
  f.name: _parse_dataclass_parse_text(
480
- f, t, cls, head=head, case_sensitive=case_sensitive, extra=extra_parsers
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,
481
506
  )
482
507
  for f, t in fields_to_serializes.items()
483
508
  }
@@ -501,12 +526,16 @@ def _parse_dataclass_split_key_value_pairs(
501
526
  *,
502
527
  list_separator: str = LIST_SEPARATOR,
503
528
  pair_separator: str = PAIR_SEPARATOR,
529
+ brackets: Iterable[tuple[str, str]] | None = BRACKETS,
504
530
  ) -> StrStrMapping:
531
+ with suppress(ExtractGroupError):
532
+ text = extract_group(r"^\{?(.*?)\}?$", text)
505
533
  try:
506
534
  return split_key_value_pairs(
507
535
  text,
508
536
  list_separator=list_separator,
509
537
  pair_separator=pair_separator,
538
+ brackets=brackets,
510
539
  mapping=True,
511
540
  )
512
541
  except _SplitKeyValuePairsSplitError as error:
utilities/parse.py CHANGED
@@ -31,7 +31,7 @@ from utilities.text import (
31
31
  split_key_value_pairs,
32
32
  split_str,
33
33
  )
34
- from utilities.types import Duration, Number, ParseObjectExtra
34
+ from utilities.types import Duration, Number, ParseObjectExtra, SerializeObjectExtra
35
35
  from utilities.typing import (
36
36
  get_args,
37
37
  is_dict_type,
@@ -240,7 +240,7 @@ def _parse_object_type(
240
240
  ) from None
241
241
  else:
242
242
  return parser(text)
243
- raise _ParseObjectParseError(type_=cls, text=text) from None
243
+ raise _ParseObjectParseError(type_=cls, text=text)
244
244
 
245
245
 
246
246
  def _parse_object_dict_type(
@@ -466,6 +466,7 @@ def serialize_object(
466
466
  *,
467
467
  list_separator: str = LIST_SEPARATOR,
468
468
  pair_separator: str = PAIR_SEPARATOR,
469
+ extra: SerializeObjectExtra | None = None,
469
470
  ) -> str:
470
471
  """Convert an object to text."""
471
472
  if (obj is None) or isinstance(
@@ -506,7 +507,9 @@ def serialize_object(
506
507
  return _serialize_object_set(
507
508
  obj, list_separator=list_separator, pair_separator=pair_separator
508
509
  )
509
- raise NotImplementedError(obj)
510
+ if extra is not None:
511
+ return _serialize_object_extra(obj, extra)
512
+ raise _SerializeObjectSerializeError(obj=obj)
510
513
 
511
514
 
512
515
  def _serialize_object_dict(
@@ -534,6 +537,19 @@ def _serialize_object_dict(
534
537
  return f"{{{joined}}}"
535
538
 
536
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
+
537
553
  def _serialize_object_list(
538
554
  obj: Sequence[Any],
539
555
  /,
@@ -585,4 +601,25 @@ def _serialize_object_tuple(
585
601
  return f"({joined})"
586
602
 
587
603
 
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
+
588
625
  __all__ = ["parse_object", "serialize_object"]
utilities/text.py CHANGED
@@ -144,7 +144,10 @@ def split_key_value_pairs(
144
144
  except SplitStrError as error:
145
145
  raise _SplitKeyValuePairsSplitError(text=text, inner=error.text) from None
146
146
  try:
147
- pairs = [split_str(text_i, separator=pair_separator, n=2) for text_i in texts]
147
+ pairs = [
148
+ split_str(text_i, separator=pair_separator, brackets=brackets, n=2)
149
+ for text_i in texts
150
+ ]
148
151
  except SplitStrError as error:
149
152
  raise _SplitKeyValuePairsSplitError(text=text, inner=error.text) from None
150
153
  if not mapping:
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",