dycw-utilities 0.109.28__py3-none-any.whl → 0.109.29__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.28
3
+ Version: 0.109.29
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=thnWMb1VwaXUYJiwR7uCyVcy6rTge0ODibDsllPsk3Q,61
1
+ utilities/__init__.py,sha256=AIZiPdrHMOKkgJqoeU29sMxSZXZMURdyCYzqvmBulyI,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
@@ -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=D1rqqrULV1FkjnCZS6804Io26_AZbMrsEILf-pZGNzw,7192
44
+ utilities/parse.py,sha256=l8W5ik1CvmPEsas-dB7cS5gCKpeyr0hQDtjZqI-9azI,16283
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
@@ -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.28.dist-info/METADATA,sha256=_wZaTQxzNcdICCzqa8Eg769N4iPa9Ho9XWHP-LLUMgw,13005
91
- dycw_utilities-0.109.28.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.109.28.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.109.28.dist-info/RECORD,,
90
+ dycw_utilities-0.109.29.dist-info/METADATA,sha256=wN-G6OupzEqFsIek2lE458u5MkSYceoapxbzRnWL8Z8,13005
91
+ dycw_utilities-0.109.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.109.29.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.109.29.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.28"
3
+ __version__ = "0.109.29"
utilities/parse.py CHANGED
@@ -7,71 +7,138 @@ 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, override
11
11
 
12
- from utilities.datetime import is_subclass_date_not_datetime
12
+ from utilities.datetime import (
13
+ is_instance_date_not_datetime,
14
+ is_subclass_date_not_datetime,
15
+ )
13
16
  from utilities.enum import ParseEnumError, parse_enum
14
17
  from utilities.functions import is_subclass_int_not_bool
15
18
  from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
16
19
  from utilities.math import ParseNumberError, parse_number
17
20
  from utilities.re import ExtractGroupError, extract_group
18
21
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
19
- from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
22
+ from utilities.text import (
23
+ ParseBoolError,
24
+ ParseNoneError,
25
+ join_strs,
26
+ parse_bool,
27
+ parse_none,
28
+ split_key_value_pairs,
29
+ split_str,
30
+ )
20
31
  from utilities.types import Duration, Number, ParseTextExtra
21
32
  from utilities.typing import (
22
33
  get_args,
34
+ is_dict_type,
35
+ is_frozenset_type,
36
+ is_list_type,
23
37
  is_literal_type,
24
38
  is_optional_type,
39
+ is_set_type,
25
40
  is_tuple_type,
26
41
  is_union_type,
27
42
  )
28
43
  from utilities.version import ParseVersionError, Version, parse_version
29
44
 
45
+ if TYPE_CHECKING:
46
+ from collections.abc import Mapping, Sequence
47
+ from collections.abc import Set as AbstractSet
48
+
30
49
 
31
50
  def parse_text(
32
- obj: Any,
51
+ type_: Any,
33
52
  text: str,
34
53
  /,
35
54
  *,
55
+ list_separator: str = ",",
56
+ pair_separator: str = "=",
36
57
  head: bool = False,
37
58
  case_sensitive: bool = False,
38
59
  extra: ParseTextExtra | None = None,
39
60
  ) -> Any:
40
61
  """Parse text."""
41
- if obj is None:
62
+ if type_ is None:
42
63
  try:
43
64
  return parse_none(text)
44
65
  except ParseNoneError:
45
- raise _ParseTextParseError(obj=obj, text=text) from None
46
- if isinstance(obj, type):
47
- return _parse_text_type(obj, text, case_sensitive=case_sensitive, extra=extra)
48
- if is_literal_type(obj):
49
- return one_str(get_args(obj), text, head=head, case_sensitive=case_sensitive)
50
- if is_optional_type(obj):
66
+ raise _ParseTextParseError(type_=type_, text=text) from None
67
+ if isinstance(type_, type):
68
+ return _parse_text_type(type_, text, case_sensitive=case_sensitive, extra=extra)
69
+ if is_dict_type(type_):
70
+ return _parse_text_dict_type(
71
+ type_,
72
+ text,
73
+ list_separator=list_separator,
74
+ pair_separator=pair_separator,
75
+ head=head,
76
+ case_sensitive=case_sensitive,
77
+ extra=extra,
78
+ )
79
+ if is_frozenset_type(type_):
80
+ return frozenset(
81
+ _parse_text_set_type(
82
+ type_,
83
+ text,
84
+ list_separator=list_separator,
85
+ pair_separator=pair_separator,
86
+ head=head,
87
+ case_sensitive=case_sensitive,
88
+ extra=extra,
89
+ )
90
+ )
91
+ if is_list_type(type_):
92
+ return _parse_text_list_type(
93
+ type_,
94
+ text,
95
+ list_separator=list_separator,
96
+ pair_separator=pair_separator,
97
+ head=head,
98
+ case_sensitive=case_sensitive,
99
+ extra=extra,
100
+ )
101
+ if is_literal_type(type_):
102
+ return one_str(get_args(type_), text, head=head, case_sensitive=case_sensitive)
103
+ if is_optional_type(type_):
51
104
  with suppress(ParseNoneError):
52
105
  return parse_none(text)
53
- inner = one(arg for arg in get_args(obj) if arg is not NoneType)
106
+ inner = one(arg for arg in get_args(type_) if arg is not NoneType)
54
107
  try:
55
108
  return parse_text(
56
- inner, text, head=head, case_sensitive=case_sensitive, extra=extra
109
+ inner,
110
+ text,
111
+ list_separator=list_separator,
112
+ pair_separator=pair_separator,
113
+ head=head,
114
+ case_sensitive=case_sensitive,
115
+ extra=extra,
57
116
  )
58
117
  except _ParseTextParseError:
59
- raise _ParseTextParseError(obj=obj, text=text) from None
60
- if is_tuple_type(obj):
61
- args = get_args(obj)
62
- try:
63
- texts = extract_group(r"^\((.*)\)$", text, flags=DOTALL).split(", ")
64
- except ExtractGroupError:
65
- raise _ParseTextParseError(obj=obj, text=text) from None
66
- if len(args) != len(texts):
67
- raise _ParseTextParseError(obj=obj, text=text)
68
- return tuple(
69
- parse_text(arg, text, head=head, case_sensitive=case_sensitive, extra=extra)
70
- for arg, text in zip(args, texts, strict=True)
118
+ raise _ParseTextParseError(type_=type_, text=text) from None
119
+ if is_set_type(type_):
120
+ return _parse_text_set_type(
121
+ type_,
122
+ text,
123
+ list_separator=list_separator,
124
+ pair_separator=pair_separator,
125
+ head=head,
126
+ case_sensitive=case_sensitive,
127
+ extra=extra,
71
128
  )
72
- if is_union_type(obj):
73
- return _parse_text_union_type(obj, text, extra=extra)
74
- raise _ParseTextParseError(obj=obj, text=text) from None
129
+ if is_tuple_type(type_):
130
+ return _parse_text_tuple_type(
131
+ type_,
132
+ text,
133
+ list_separator=list_separator,
134
+ pair_separator=pair_separator,
135
+ head=head,
136
+ case_sensitive=case_sensitive,
137
+ extra=extra,
138
+ )
139
+ if is_union_type(type_):
140
+ return _parse_text_union_type(type_, text, extra=extra)
141
+ raise _ParseTextParseError(type_=type_, text=text) from None
75
142
 
76
143
 
77
144
  def _parse_text_type(
@@ -87,69 +154,69 @@ def _parse_text_type(
87
154
  try:
88
155
  return parse_none(text)
89
156
  except ParseNoneError:
90
- raise _ParseTextParseError(obj=cls, text=text) from None
157
+ raise _ParseTextParseError(type_=cls, text=text) from None
91
158
  if issubclass(cls, str):
92
159
  return text
93
160
  if issubclass(cls, bool):
94
161
  try:
95
162
  return parse_bool(text)
96
163
  except ParseBoolError:
97
- raise _ParseTextParseError(obj=cls, text=text) from None
164
+ raise _ParseTextParseError(type_=cls, text=text) from None
98
165
  if is_subclass_int_not_bool(cls):
99
166
  try:
100
167
  return int(text)
101
168
  except ValueError:
102
- raise _ParseTextParseError(obj=cls, text=text) from None
169
+ raise _ParseTextParseError(type_=cls, text=text) from None
103
170
  if issubclass(cls, float):
104
171
  try:
105
172
  return float(text)
106
173
  except ValueError:
107
- raise _ParseTextParseError(obj=cls, text=text) from None
174
+ raise _ParseTextParseError(type_=cls, text=text) from None
108
175
  if issubclass(cls, Enum):
109
176
  try:
110
177
  return parse_enum(text, cls, case_sensitive=case_sensitive)
111
178
  except ParseEnumError:
112
- raise _ParseTextParseError(obj=cls, text=text) from None
179
+ raise _ParseTextParseError(type_=cls, text=text) from None
113
180
  if issubclass(cls, Path):
114
181
  return Path(text).expanduser()
115
182
  if issubclass(cls, Sentinel):
116
183
  try:
117
184
  return parse_sentinel(text)
118
185
  except ParseSentinelError:
119
- raise _ParseTextParseError(obj=cls, text=text) from None
186
+ raise _ParseTextParseError(type_=cls, text=text) from None
120
187
  if issubclass(cls, Version):
121
188
  try:
122
189
  return parse_version(text)
123
190
  except ParseVersionError:
124
- raise _ParseTextParseError(obj=cls, text=text) from None
191
+ raise _ParseTextParseError(type_=cls, text=text) from None
125
192
  if is_subclass_date_not_datetime(cls):
126
193
  from utilities.whenever import ParseDateError, parse_date
127
194
 
128
195
  try:
129
196
  return parse_date(text)
130
197
  except ParseDateError:
131
- raise _ParseTextParseError(obj=cls, text=text) from None
198
+ raise _ParseTextParseError(type_=cls, text=text) from None
132
199
  if issubclass(cls, dt.datetime):
133
200
  from utilities.whenever import ParseDateTimeError, parse_datetime
134
201
 
135
202
  try:
136
203
  return parse_datetime(text)
137
204
  except ParseDateTimeError:
138
- raise _ParseTextParseError(obj=cls, text=text) from None
205
+ raise _ParseTextParseError(type_=cls, text=text) from None
139
206
  if issubclass(cls, dt.time):
140
207
  from utilities.whenever import ParseTimeError, parse_time
141
208
 
142
209
  try:
143
210
  return parse_time(text)
144
211
  except ParseTimeError:
145
- raise _ParseTextParseError(obj=cls, text=text) from None
212
+ raise _ParseTextParseError(type_=cls, text=text) from None
146
213
  if issubclass(cls, dt.timedelta):
147
214
  from utilities.whenever import ParseTimedeltaError, parse_timedelta
148
215
 
149
216
  try:
150
217
  return parse_timedelta(text)
151
218
  except ParseTimedeltaError:
152
- raise _ParseTextParseError(obj=cls, text=text) from None
219
+ raise _ParseTextParseError(type_=cls, text=text) from None
153
220
  if extra is not None:
154
221
  try:
155
222
  parser = one(p for c, p in extra.items() if issubclass(cls, c))
@@ -157,41 +224,197 @@ def _parse_text_type(
157
224
  pass
158
225
  except OneNonUniqueError as error:
159
226
  raise _ParseTextExtraNonUniqueError(
160
- obj=cls, text=text, first=error.first, second=error.second
227
+ type_=cls, text=text, first=error.first, second=error.second
161
228
  ) from None
162
229
  else:
163
230
  return parser(text)
164
- raise _ParseTextParseError(obj=cls, text=text) from None
231
+ raise _ParseTextParseError(type_=cls, text=text) from None
232
+
233
+
234
+ def _parse_text_dict_type(
235
+ type_: Any,
236
+ text: str,
237
+ /,
238
+ *,
239
+ list_separator: str = ",",
240
+ pair_separator: str = "=",
241
+ head: bool = False,
242
+ case_sensitive: bool = False,
243
+ extra: ParseTextExtra | None = None,
244
+ ) -> dict[Any, Any]:
245
+ key_type, value_type = get_args(type_)
246
+ try:
247
+ inner_text = extract_group(r"^{(.*)}$", text, flags=DOTALL)
248
+ except ExtractGroupError:
249
+ raise _ParseTextParseError(type_=type_, text=text) from None
250
+ pairs = split_key_value_pairs(
251
+ inner_text,
252
+ list_separator=list_separator,
253
+ pair_separator=pair_separator,
254
+ mapping=True,
255
+ )
256
+ keys = (
257
+ parse_text(
258
+ key_type,
259
+ k,
260
+ list_separator=list_separator,
261
+ pair_separator=pair_separator,
262
+ head=head,
263
+ case_sensitive=case_sensitive,
264
+ extra=extra,
265
+ )
266
+ for k in pairs
267
+ )
268
+ values = (
269
+ parse_text(
270
+ value_type,
271
+ v,
272
+ list_separator=list_separator,
273
+ pair_separator=pair_separator,
274
+ head=head,
275
+ case_sensitive=case_sensitive,
276
+ extra=extra,
277
+ )
278
+ for v in pairs.values()
279
+ )
280
+ try:
281
+ return dict(zip(keys, values, strict=True))
282
+ except _ParseTextParseError:
283
+ raise _ParseTextParseError(type_=type_, text=text) from None
284
+
285
+
286
+ def _parse_text_list_type(
287
+ type_: Any,
288
+ text: str,
289
+ /,
290
+ *,
291
+ list_separator: str = ",",
292
+ pair_separator: str = "=",
293
+ head: bool = False,
294
+ case_sensitive: bool = False,
295
+ extra: ParseTextExtra | None = None,
296
+ ) -> list[Any]:
297
+ inner_type = one(get_args(type_))
298
+ try:
299
+ inner_text = extract_group(r"^\[(.*)\]$", text, flags=DOTALL)
300
+ except ExtractGroupError:
301
+ raise _ParseTextParseError(type_=type_, text=text) from None
302
+ texts = split_str(inner_text, separator=list_separator)
303
+ try:
304
+ return [
305
+ parse_text(
306
+ inner_type,
307
+ t,
308
+ list_separator=list_separator,
309
+ pair_separator=pair_separator,
310
+ head=head,
311
+ case_sensitive=case_sensitive,
312
+ extra=extra,
313
+ )
314
+ for t in texts
315
+ ]
316
+ except _ParseTextParseError:
317
+ raise _ParseTextParseError(type_=type_, text=text) from None
318
+
319
+
320
+ def _parse_text_set_type(
321
+ type_: Any,
322
+ text: str,
323
+ /,
324
+ *,
325
+ list_separator: str = ",",
326
+ pair_separator: str = "=",
327
+ head: bool = False,
328
+ case_sensitive: bool = False,
329
+ extra: ParseTextExtra | None = None,
330
+ ) -> set[Any]:
331
+ inner_type = one(get_args(type_))
332
+ try:
333
+ inner_text = extract_group(r"^{(.*)}$", text, flags=DOTALL)
334
+ except ExtractGroupError:
335
+ raise _ParseTextParseError(type_=type_, text=text) from None
336
+ texts = split_str(inner_text, separator=list_separator)
337
+ try:
338
+ return {
339
+ parse_text(
340
+ inner_type,
341
+ t,
342
+ list_separator=list_separator,
343
+ pair_separator=pair_separator,
344
+ head=head,
345
+ case_sensitive=case_sensitive,
346
+ extra=extra,
347
+ )
348
+ for t in texts
349
+ }
350
+ except _ParseTextParseError:
351
+ raise _ParseTextParseError(type_=type_, text=text) from None
165
352
 
166
353
 
167
354
  def _parse_text_union_type(
168
- obj: Any, text: str, /, *, extra: ParseTextExtra | None = None
355
+ type_: Any, text: str, /, *, extra: ParseTextExtra | None = None
169
356
  ) -> Any:
170
- if obj is Number:
357
+ if type_ is Number:
171
358
  try:
172
359
  return parse_number(text)
173
360
  except ParseNumberError:
174
- raise _ParseTextParseError(obj=obj, text=text) from None
175
- if obj is Duration:
361
+ raise _ParseTextParseError(type_=type_, text=text) from None
362
+ if type_ is Duration:
176
363
  from utilities.whenever import ParseDurationError, parse_duration
177
364
 
178
365
  try:
179
366
  return parse_duration(text)
180
367
  except ParseDurationError:
181
- raise _ParseTextParseError(obj=obj, text=text) from None
368
+ raise _ParseTextParseError(type_=type_, text=text) from None
182
369
  if extra is not None:
183
370
  try:
184
- parser = one(p for c, p in extra.items() if c is obj)
371
+ parser = one(p for c, p in extra.items() if c is type_)
185
372
  except OneEmptyError:
186
373
  pass
187
374
  else:
188
375
  return parser(text)
189
- raise _ParseTextParseError(obj=obj, text=text) from None
376
+ raise _ParseTextParseError(type_=type_, text=text) from None
377
+
378
+
379
+ def _parse_text_tuple_type(
380
+ type_: Any,
381
+ text: str,
382
+ /,
383
+ *,
384
+ list_separator: str = ",",
385
+ pair_separator: str = "=",
386
+ head: bool = False,
387
+ case_sensitive: bool = False,
388
+ extra: ParseTextExtra | None = None,
389
+ ) -> tuple[Any, ...]:
390
+ args = get_args(type_)
391
+ try:
392
+ inner = extract_group(r"^\((.*)\)$", text, flags=DOTALL)
393
+ except ExtractGroupError:
394
+ raise _ParseTextParseError(type_=type_, text=text) from None
395
+ texts = inner.split(",")
396
+ if len(args) != len(texts):
397
+ raise _ParseTextParseError(type_=type_, text=text)
398
+ try:
399
+ return tuple(
400
+ parse_text(
401
+ arg,
402
+ text,
403
+ list_separator=list_separator,
404
+ pair_separator=pair_separator,
405
+ head=head,
406
+ case_sensitive=case_sensitive,
407
+ extra=extra,
408
+ )
409
+ for arg, text in zip(args, texts, strict=True)
410
+ )
411
+ except _ParseTextParseError:
412
+ raise _ParseTextParseError(type_=type_, text=text) from None
190
413
 
191
414
 
192
415
  @dataclass
193
416
  class ParseTextError(Exception):
194
- obj: Any
417
+ type_: Any
195
418
  text: str
196
419
 
197
420
 
@@ -199,7 +422,7 @@ class ParseTextError(Exception):
199
422
  class _ParseTextParseError(ParseTextError):
200
423
  @override
201
424
  def __str__(self) -> str:
202
- return f"Unable to parse {self.obj!r}; got {self.text!r}"
425
+ return f"Unable to parse {self.type_!r}; got {self.text!r}"
203
426
 
204
427
 
205
428
  @dataclass
@@ -209,4 +432,105 @@ class _ParseTextExtraNonUniqueError(ParseTextError):
209
432
 
210
433
  @override
211
434
  def __str__(self) -> str:
212
- 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"
435
+ return f"Unable to parse {self.type_!r} since `extra` must contain exactly one parent class; got {self.first!r}, {self.second!r} and perhaps more"
436
+
437
+
438
+ ##
439
+
440
+
441
+ def to_text(
442
+ obj: Any, /, *, list_separator: str = ",", pair_separator: str = "="
443
+ ) -> str:
444
+ """Convert an object to text."""
445
+ if (obj is None) or isinstance(
446
+ obj, bool | int | float | str | Path | Sentinel | Version
447
+ ):
448
+ return str(obj)
449
+ if is_instance_date_not_datetime(obj):
450
+ from utilities.whenever import serialize_date
451
+
452
+ return serialize_date(obj)
453
+ if isinstance(obj, dt.datetime):
454
+ from utilities.whenever import serialize_datetime
455
+
456
+ return serialize_datetime(obj)
457
+ if isinstance(obj, dt.time):
458
+ from utilities.whenever import serialize_time
459
+
460
+ return serialize_time(obj)
461
+ if isinstance(obj, dt.timedelta):
462
+ from utilities.whenever import serialize_timedelta
463
+
464
+ return serialize_timedelta(obj)
465
+ if isinstance(obj, Enum):
466
+ return obj.name
467
+ if isinstance(obj, dict):
468
+ return _to_text_dict(
469
+ obj, list_separator=list_separator, pair_separator=pair_separator
470
+ )
471
+ if isinstance(obj, list):
472
+ return _to_text_list(
473
+ obj, list_separator=list_separator, pair_separator=pair_separator
474
+ )
475
+ if isinstance(obj, tuple):
476
+ return _to_text_tuple(
477
+ obj, list_separator=list_separator, pair_separator=pair_separator
478
+ )
479
+ if isinstance(obj, set | frozenset):
480
+ return _to_text_set(
481
+ obj, list_separator=list_separator, pair_separator=pair_separator
482
+ )
483
+ raise NotImplementedError(obj)
484
+
485
+
486
+ def _to_text_dict(
487
+ obj: Mapping[Any, Any], /, *, list_separator: str = ",", pair_separator: str = "="
488
+ ) -> str:
489
+ keys = (
490
+ to_text(k, list_separator=list_separator, pair_separator=pair_separator)
491
+ for k in obj
492
+ )
493
+ values = (
494
+ to_text(v, list_separator=list_separator, pair_separator=pair_separator)
495
+ for v in obj.values()
496
+ )
497
+ items = zip(keys, values, strict=True)
498
+ joined_items = (join_strs(item, separator=pair_separator) for item in items)
499
+ joined = join_strs(joined_items, separator=list_separator)
500
+ return f"{{{joined}}}"
501
+
502
+
503
+ def _to_text_list(
504
+ obj: Sequence[Any], /, *, list_separator: str = ",", pair_separator: str = "="
505
+ ) -> str:
506
+ items = (
507
+ to_text(i, list_separator=list_separator, pair_separator=pair_separator)
508
+ for i in obj
509
+ )
510
+ joined = join_strs(items, separator=list_separator)
511
+ return f"[{joined}]"
512
+
513
+
514
+ def _to_text_set(
515
+ obj: AbstractSet[Any], /, *, list_separator: str = ",", pair_separator: str = "="
516
+ ) -> str:
517
+ items = (
518
+ to_text(i, list_separator=list_separator, pair_separator=pair_separator)
519
+ for i in obj
520
+ )
521
+ joined = join_strs(items, sort=True, separator=list_separator)
522
+ return f"{{{joined}}}"
523
+
524
+
525
+ def _to_text_tuple(
526
+ obj: tuple[Any, ...], /, *, list_separator: str = ",", pair_separator: str = "="
527
+ ) -> str:
528
+ items = (
529
+ to_text(i, list_separator=list_separator, pair_separator=pair_separator)
530
+ for i in obj
531
+ )
532
+ joined = join_strs(items, separator=list_separator)
533
+ return f"({joined})"
534
+
535
+
536
+ __all__ = ["parse_text"]