dycw-utilities 0.109.24__py3-none-any.whl → 0.109.26__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.24
3
+ Version: 0.109.26
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=lTcMVKZy93oLWx7dgqyW_K3S_kxMyIwptBXIvNDLsl8,61
1
+ utilities/__init__.py,sha256=u3bOXUZkUtu44-KigskBo_7SPJ0LgayUw3iTPoGATOo,61
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=rQS-G-JwhQNufV80aThSOD_ENliueqkz4N6NqtTTPN8,23294
14
+ utilities/dataclasses.py,sha256=8-38WHrScAvElBNvFxBnhJwab1XXkSXpDOiNPOAvh2Q,23295
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
@@ -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=fKPnh_wNFRXF1mM1CkTUUlBUrAj7Mf2xDGnHfrUepQs,4775
76
+ utilities/text.py,sha256=Ax_n-nY80_onWxag9M0PkmbaAqwyut9AEA9tEMd5lBs,6694
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=fkaqL67A_1p0-Q88JTu1dt_GV3mVo5mSslYa9DGWNq4,17903
80
+ utilities/types.py,sha256=z1hbBOT5TkzTn2JOvSldw6DScxi3erG9qpJ3xci66GI,17963
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.109.24.dist-info/METADATA,sha256=qsqFi_RWRE2umffrznAhmCyC9F_zn5VwAbpMoaqO8iw,13005
91
- dycw_utilities-0.109.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.109.24.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.109.24.dist-info/RECORD,,
90
+ dycw_utilities-0.109.26.dist-info/METADATA,sha256=aJEgBNp03JxSJvRWENUjCb_0CqvmCZktjl1S-XNYfDQ,13005
91
+ dycw_utilities-0.109.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.109.26.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.109.26.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.24"
3
+ __version__ = "0.109.26"
utilities/dataclasses.py CHANGED
@@ -24,7 +24,7 @@ from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
24
24
  from utilities.operator import is_equal
25
25
  from utilities.parse import ParseTextError, parse_text
26
26
  from utilities.sentinel import Sentinel, sentinel
27
- from utilities.types import ParseTextExtra, TDataclass
27
+ from utilities.types import ParseTextExtra, StrStrMapping, TDataclass
28
28
  from utilities.typing import get_type_hints
29
29
 
30
30
  if TYPE_CHECKING:
@@ -444,7 +444,7 @@ class StrMappingToFieldMappingError(Exception):
444
444
 
445
445
 
446
446
  def text_to_dataclass(
447
- text_or_mapping: str | Mapping[str, str],
447
+ text_or_mapping: str | StrStrMapping,
448
448
  cls: type[TDataclass],
449
449
  /,
450
450
  *,
@@ -499,9 +499,7 @@ def text_to_dataclass(
499
499
  )
500
500
 
501
501
 
502
- def _text_to_dataclass_split_text(
503
- text: str, cls: type[TDataclass], /
504
- ) -> Mapping[str, str]:
502
+ def _text_to_dataclass_split_text(text: str, cls: type[TDataclass], /) -> StrStrMapping:
505
503
  pairs = (t for t in text.split(",") if t != "")
506
504
  return dict(_text_to_dataclass_split_key_value_pair(pair, cls) for pair in pairs)
507
505
 
utilities/text.py CHANGED
@@ -6,8 +6,13 @@ from re import IGNORECASE, Match, search
6
6
  from textwrap import dedent
7
7
  from typing import TYPE_CHECKING, Any, Literal, overload, override
8
8
 
9
+ from utilities.iterables import CheckDuplicatesError, check_duplicates
10
+ from utilities.reprlib import get_repr
11
+
9
12
  if TYPE_CHECKING:
10
- from collections.abc import Iterable, Sequence
13
+ from collections.abc import Iterable, Mapping, Sequence
14
+
15
+ from utilities.types import StrStrMapping
11
16
 
12
17
 
13
18
  def parse_bool(text: str, /) -> bool:
@@ -81,14 +86,81 @@ def _snake_case_title(match: Match[str], /) -> str:
81
86
  ##
82
87
 
83
88
 
89
+ @overload
90
+ def split_key_value_pairs(
91
+ text: str,
92
+ /,
93
+ *,
94
+ list_separator: str = ",",
95
+ pair_separator: str = "=",
96
+ mapping: Literal[True],
97
+ ) -> StrStrMapping: ...
98
+ @overload
99
+ def split_key_value_pairs(
100
+ text: str,
101
+ /,
102
+ *,
103
+ list_separator: str = ",",
104
+ pair_separator: str = "=",
105
+ mapping: Literal[False] = False,
106
+ ) -> Sequence[tuple[str, str]]: ...
107
+ @overload
108
+ def split_key_value_pairs(
109
+ text: str,
110
+ /,
111
+ *,
112
+ list_separator: str = ",",
113
+ pair_separator: str = "=",
114
+ mapping: bool = False,
115
+ ) -> Sequence[tuple[str, str]] | StrStrMapping: ...
84
116
  def split_key_value_pairs(
85
- text: str, /, *, list_separator: str = ",", pair_separator: str = "="
86
- ) -> Sequence[tuple[str, str]]:
117
+ text: str,
118
+ /,
119
+ *,
120
+ list_separator: str = ",",
121
+ pair_separator: str = "=",
122
+ mapping: bool = False,
123
+ ) -> Sequence[tuple[str, str]] | StrStrMapping:
87
124
  """Split a string into key-value pairs."""
88
- return [
89
- split_str(text_i, separator=pair_separator, n=2)
90
- for text_i in split_str(text, separator=list_separator)
91
- ]
125
+ try:
126
+ pairs = [
127
+ split_str(text_i, separator=pair_separator, n=2)
128
+ for text_i in split_str(text, separator=list_separator)
129
+ ]
130
+ except SplitStrError as error:
131
+ raise _SplitKeyValuePairsSplitError(text=text, inner=error.text) from None
132
+ if not mapping:
133
+ return pairs
134
+ try:
135
+ check_duplicates(k for k, _ in pairs)
136
+ except CheckDuplicatesError as error:
137
+ raise _SplitKeyValuePairsDuplicateKeysError(
138
+ text=text, counts=error.counts
139
+ ) from None
140
+ return dict(pairs)
141
+
142
+
143
+ @dataclass(kw_only=True, slots=True)
144
+ class SplitKeyValuePairsError(Exception):
145
+ text: str
146
+
147
+
148
+ @dataclass(kw_only=True, slots=True)
149
+ class _SplitKeyValuePairsSplitError(SplitKeyValuePairsError):
150
+ inner: str
151
+
152
+ @override
153
+ def __str__(self) -> str:
154
+ return f"Unable to split {self.text!r} into key-value pairs; got {self.inner!r}"
155
+
156
+
157
+ @dataclass(kw_only=True, slots=True)
158
+ class _SplitKeyValuePairsDuplicateKeysError(SplitKeyValuePairsError):
159
+ counts: Mapping[str, int]
160
+
161
+ @override
162
+ def __str__(self) -> str:
163
+ return f"Unable to split {self.text!r} into a mapping since there are duplicate keys; got {get_repr(self.counts)}"
92
164
 
93
165
 
94
166
  ##
@@ -182,6 +254,7 @@ def strip_and_dedent(text: str, /, *, trailing: bool = False) -> str:
182
254
  __all__ = [
183
255
  "ParseBoolError",
184
256
  "ParseNoneError",
257
+ "SplitKeyValuePairsError",
185
258
  "SplitStrError",
186
259
  "join_strs",
187
260
  "parse_bool",
utilities/types.py CHANGED
@@ -46,6 +46,7 @@ type OpenMode = Literal[
46
46
  "a+b",
47
47
  ]
48
48
  type StrMapping = Mapping[str, Any]
49
+ type StrStrMapping = Mapping[str, str]
49
50
  type TupleOrStrMapping = tuple[Any, ...] | StrMapping
50
51
  type MaybeCallable[_T] = _T | Callable[[], _T]
51
52
  type MaybeStr[_T] = _T | str
@@ -288,6 +289,7 @@ __all__ = [
288
289
  "RoundMode",
289
290
  "Seed",
290
291
  "StrMapping",
292
+ "StrStrMapping",
291
293
  "SupportsAbs",
292
294
  "SupportsAdd",
293
295
  "SupportsBytes",