dycw-utilities 0.109.22__py3-none-any.whl → 0.109.24__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.109.22.dist-info → dycw_utilities-0.109.24.dist-info}/METADATA +1 -1
- {dycw_utilities-0.109.22.dist-info → dycw_utilities-0.109.24.dist-info}/RECORD +8 -8
- utilities/__init__.py +1 -1
- utilities/click.py +35 -51
- utilities/polars.py +16 -0
- utilities/text.py +81 -25
- {dycw_utilities-0.109.22.dist-info → dycw_utilities-0.109.24.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.109.22.dist-info → dycw_utilities-0.109.24.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,11 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=lTcMVKZy93oLWx7dgqyW_K3S_kxMyIwptBXIvNDLsl8,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
|
5
5
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
6
6
|
utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
|
7
7
|
utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
|
8
|
-
utilities/click.py,sha256=
|
8
|
+
utilities/click.py,sha256=lIkJIp_3vaoqsvADI4azoLgEWqlUHVRWBQePLbPrcPo,14243
|
9
9
|
utilities/concurrent.py,sha256=s2scTEd2AhXVTW4hpASU2qxV_DiVLALfms55cCQzCvM,2886
|
10
10
|
utilities/contextlib.py,sha256=OOIIEa5lXKGzFAnauaul40nlQnQko6Na4ryiMJcHkIg,478
|
11
11
|
utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
@@ -46,7 +46,7 @@ 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
|
48
48
|
utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
|
49
|
-
utilities/polars.py,sha256=
|
49
|
+
utilities/polars.py,sha256=woTGhyzXNLN30SQgwXz54-I1aJp1oWATJ2rpmke7gKI,58419
|
50
50
|
utilities/polars_ols.py,sha256=efhXf0gjrHUpQrvS6a7g8yJQJWf_ATKtJnqqF2inCOU,5680
|
51
51
|
utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
|
52
52
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -73,7 +73,7 @@ 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=fKPnh_wNFRXF1mM1CkTUUlBUrAj7Mf2xDGnHfrUepQs,4775
|
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
|
@@ -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.
|
91
|
-
dycw_utilities-0.109.
|
92
|
-
dycw_utilities-0.109.
|
93
|
-
dycw_utilities-0.109.
|
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,,
|
utilities/__init__.py
CHANGED
utilities/click.py
CHANGED
@@ -21,7 +21,6 @@ from utilities.datetime import EnsureMonthError, MonthLike, ensure_month
|
|
21
21
|
from utilities.enum import EnsureEnumError, ensure_enum
|
22
22
|
from utilities.functions import EnsureStrError, ensure_str, get_class_name
|
23
23
|
from utilities.iterables import is_iterable_not_str
|
24
|
-
from utilities.sentinel import SENTINEL_REPR
|
25
24
|
from utilities.text import split_str
|
26
25
|
from utilities.types import (
|
27
26
|
DateLike,
|
@@ -245,13 +244,10 @@ class ZonedDateTime(ParamType):
|
|
245
244
|
class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
|
246
245
|
"""A frozenset-valued parameter."""
|
247
246
|
|
248
|
-
def __init__(
|
249
|
-
self, param: _TParam, /, *, separator: str = ",", empty: str = SENTINEL_REPR
|
250
|
-
) -> None:
|
247
|
+
def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
|
251
248
|
self.name = f"FROZENSET[{param.name}]"
|
252
249
|
self._param = param
|
253
250
|
self._separator = separator
|
254
|
-
self._empty = empty
|
255
251
|
super().__init__()
|
256
252
|
|
257
253
|
@override
|
@@ -273,7 +269,7 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
|
|
273
269
|
text = ensure_str(value)
|
274
270
|
except EnsureStrError as error:
|
275
271
|
return self.fail(str(error), param=param, ctx=ctx)
|
276
|
-
values = split_str(text, separator=self._separator
|
272
|
+
values = split_str(text, separator=self._separator)
|
277
273
|
return frozenset(self._param.convert(v, param, ctx) for v in values)
|
278
274
|
|
279
275
|
@override
|
@@ -290,15 +286,15 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
|
|
290
286
|
class FrozenSetBools(FrozenSetParameter[BoolParamType, str]):
|
291
287
|
"""A frozenset-of-bools-valued parameter."""
|
292
288
|
|
293
|
-
def __init__(self, *, separator: str = ","
|
294
|
-
super().__init__(BoolParamType(), separator=separator
|
289
|
+
def __init__(self, *, separator: str = ",") -> None:
|
290
|
+
super().__init__(BoolParamType(), separator=separator)
|
295
291
|
|
296
292
|
|
297
293
|
class FrozenSetDates(FrozenSetParameter[Date, dt.date]):
|
298
294
|
"""A frozenset-of-dates-valued parameter."""
|
299
295
|
|
300
|
-
def __init__(self, *, separator: str = ","
|
301
|
-
super().__init__(Date(), separator=separator
|
296
|
+
def __init__(self, *, separator: str = ",") -> None:
|
297
|
+
super().__init__(Date(), separator=separator)
|
302
298
|
|
303
299
|
|
304
300
|
class FrozenSetChoices(FrozenSetParameter[Choice, str]):
|
@@ -311,12 +307,9 @@ class FrozenSetChoices(FrozenSetParameter[Choice, str]):
|
|
311
307
|
*,
|
312
308
|
case_sensitive: bool = False,
|
313
309
|
separator: str = ",",
|
314
|
-
empty: str = SENTINEL_REPR,
|
315
310
|
) -> None:
|
316
311
|
super().__init__(
|
317
|
-
Choice(choices, case_sensitive=case_sensitive),
|
318
|
-
separator=separator,
|
319
|
-
empty=empty,
|
312
|
+
Choice(choices, case_sensitive=case_sensitive), separator=separator
|
320
313
|
)
|
321
314
|
|
322
315
|
|
@@ -330,46 +323,43 @@ class FrozenSetEnums(FrozenSetParameter[Enum[TEnum], TEnum]):
|
|
330
323
|
*,
|
331
324
|
case_sensitive: bool = False,
|
332
325
|
separator: str = ",",
|
333
|
-
empty: str = SENTINEL_REPR,
|
334
326
|
) -> None:
|
335
|
-
super().__init__(
|
336
|
-
Enum(enum, case_sensitive=case_sensitive), separator=separator, empty=empty
|
337
|
-
)
|
327
|
+
super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
|
338
328
|
|
339
329
|
|
340
330
|
class FrozenSetFloats(FrozenSetParameter[FloatParamType, float]):
|
341
331
|
"""A frozenset-of-floats-valued parameter."""
|
342
332
|
|
343
|
-
def __init__(self, *, separator: str = ","
|
344
|
-
super().__init__(FloatParamType(), separator=separator
|
333
|
+
def __init__(self, *, separator: str = ",") -> None:
|
334
|
+
super().__init__(FloatParamType(), separator=separator)
|
345
335
|
|
346
336
|
|
347
337
|
class FrozenSetInts(FrozenSetParameter[IntParamType, int]):
|
348
338
|
"""A frozenset-of-ints-valued parameter."""
|
349
339
|
|
350
|
-
def __init__(self, *, separator: str = ","
|
351
|
-
super().__init__(IntParamType(), separator=separator
|
340
|
+
def __init__(self, *, separator: str = ",") -> None:
|
341
|
+
super().__init__(IntParamType(), separator=separator)
|
352
342
|
|
353
343
|
|
354
344
|
class FrozenSetMonths(FrozenSetParameter[Month, utilities.datetime.Month]):
|
355
345
|
"""A frozenset-of-months-valued parameter."""
|
356
346
|
|
357
|
-
def __init__(self, *, separator: str = ","
|
358
|
-
super().__init__(Month(), separator=separator
|
347
|
+
def __init__(self, *, separator: str = ",") -> None:
|
348
|
+
super().__init__(Month(), separator=separator)
|
359
349
|
|
360
350
|
|
361
351
|
class FrozenSetStrs(FrozenSetParameter[StringParamType, str]):
|
362
352
|
"""A frozenset-of-strs-valued parameter."""
|
363
353
|
|
364
|
-
def __init__(self, *, separator: str = ","
|
365
|
-
super().__init__(StringParamType(), separator=separator
|
354
|
+
def __init__(self, *, separator: str = ",") -> None:
|
355
|
+
super().__init__(StringParamType(), separator=separator)
|
366
356
|
|
367
357
|
|
368
358
|
class FrozenSetUUIDs(FrozenSetParameter[UUIDParameterType, UUID]):
|
369
359
|
"""A frozenset-of-UUIDs-valued parameter."""
|
370
360
|
|
371
|
-
def __init__(self, *, separator: str = ","
|
372
|
-
super().__init__(UUIDParameterType(), separator=separator
|
361
|
+
def __init__(self, *, separator: str = ",") -> None:
|
362
|
+
super().__init__(UUIDParameterType(), separator=separator)
|
373
363
|
|
374
364
|
|
375
365
|
# parameters - list
|
@@ -378,13 +368,10 @@ class FrozenSetUUIDs(FrozenSetParameter[UUIDParameterType, UUID]):
|
|
378
368
|
class ListParameter(ParamType, Generic[_TParam, _T]):
|
379
369
|
"""A list-valued parameter."""
|
380
370
|
|
381
|
-
def __init__(
|
382
|
-
self, param: _TParam, /, *, separator: str = ",", empty: str = SENTINEL_REPR
|
383
|
-
) -> None:
|
371
|
+
def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
|
384
372
|
self.name = f"LIST[{param.name}]"
|
385
373
|
self._param = param
|
386
374
|
self._separator = separator
|
387
|
-
self._empty = empty
|
388
375
|
super().__init__()
|
389
376
|
|
390
377
|
@override
|
@@ -406,7 +393,7 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
|
|
406
393
|
text = ensure_str(value)
|
407
394
|
except EnsureStrError as error:
|
408
395
|
return self.fail(str(error), param=param, ctx=ctx)
|
409
|
-
values = split_str(text, separator=self._separator
|
396
|
+
values = split_str(text, separator=self._separator)
|
410
397
|
return [self._param.convert(v, param, ctx) for v in values]
|
411
398
|
|
412
399
|
@override
|
@@ -423,15 +410,15 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
|
|
423
410
|
class ListBools(ListParameter[BoolParamType, str]):
|
424
411
|
"""A list-of-bools-valued parameter."""
|
425
412
|
|
426
|
-
def __init__(self, *, separator: str = ","
|
427
|
-
super().__init__(BoolParamType(), separator=separator
|
413
|
+
def __init__(self, *, separator: str = ",") -> None:
|
414
|
+
super().__init__(BoolParamType(), separator=separator)
|
428
415
|
|
429
416
|
|
430
417
|
class ListDates(ListParameter[Date, dt.date]):
|
431
418
|
"""A list-of-dates-valued parameter."""
|
432
419
|
|
433
|
-
def __init__(self, *, separator: str = ","
|
434
|
-
super().__init__(Date(), separator=separator
|
420
|
+
def __init__(self, *, separator: str = ",") -> None:
|
421
|
+
super().__init__(Date(), separator=separator)
|
435
422
|
|
436
423
|
|
437
424
|
class ListEnums(ListParameter[Enum[TEnum], TEnum]):
|
@@ -444,46 +431,43 @@ class ListEnums(ListParameter[Enum[TEnum], TEnum]):
|
|
444
431
|
*,
|
445
432
|
case_sensitive: bool = False,
|
446
433
|
separator: str = ",",
|
447
|
-
empty: str = SENTINEL_REPR,
|
448
434
|
) -> None:
|
449
|
-
super().__init__(
|
450
|
-
Enum(enum, case_sensitive=case_sensitive), separator=separator, empty=empty
|
451
|
-
)
|
435
|
+
super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
|
452
436
|
|
453
437
|
|
454
438
|
class ListFloats(ListParameter[FloatParamType, float]):
|
455
439
|
"""A list-of-floats-valued parameter."""
|
456
440
|
|
457
|
-
def __init__(self, *, separator: str = ","
|
458
|
-
super().__init__(FloatParamType(), separator=separator
|
441
|
+
def __init__(self, *, separator: str = ",") -> None:
|
442
|
+
super().__init__(FloatParamType(), separator=separator)
|
459
443
|
|
460
444
|
|
461
445
|
class ListInts(ListParameter[IntParamType, int]):
|
462
446
|
"""A list-of-ints-valued parameter."""
|
463
447
|
|
464
|
-
def __init__(self, *, separator: str = ","
|
465
|
-
super().__init__(IntParamType(), separator=separator
|
448
|
+
def __init__(self, *, separator: str = ",") -> None:
|
449
|
+
super().__init__(IntParamType(), separator=separator)
|
466
450
|
|
467
451
|
|
468
452
|
class ListMonths(ListParameter[Month, utilities.datetime.Month]):
|
469
453
|
"""A list-of-months-valued parameter."""
|
470
454
|
|
471
|
-
def __init__(self, *, separator: str = ","
|
472
|
-
super().__init__(Month(), separator=separator
|
455
|
+
def __init__(self, *, separator: str = ",") -> None:
|
456
|
+
super().__init__(Month(), separator=separator)
|
473
457
|
|
474
458
|
|
475
459
|
class ListStrs(ListParameter[StringParamType, str]):
|
476
460
|
"""A list-of-strs-valued parameter."""
|
477
461
|
|
478
|
-
def __init__(self, *, separator: str = ","
|
479
|
-
super().__init__(StringParamType(), separator=separator
|
462
|
+
def __init__(self, *, separator: str = ",") -> None:
|
463
|
+
super().__init__(StringParamType(), separator=separator)
|
480
464
|
|
481
465
|
|
482
466
|
class ListUUIDs(ListParameter[UUIDParameterType, UUID]):
|
483
467
|
"""A list-of-UUIDs-valued parameter."""
|
484
468
|
|
485
|
-
def __init__(self, *, separator: str = ","
|
486
|
-
super().__init__(UUIDParameterType(), separator=separator
|
469
|
+
def __init__(self, *, separator: str = ",") -> None:
|
470
|
+
super().__init__(UUIDParameterType(), separator=separator)
|
487
471
|
|
488
472
|
|
489
473
|
# private
|
utilities/polars.py
CHANGED
@@ -1228,6 +1228,21 @@ class _GetDataTypeOrSeriesTimeZoneNotZonedError(GetDataTypeOrSeriesTimeZoneError
|
|
1228
1228
|
##
|
1229
1229
|
|
1230
1230
|
|
1231
|
+
def get_expr_name(obj: Series | DataFrame, expr: Expr, /) -> str:
|
1232
|
+
"""Get the name of an expression."""
|
1233
|
+
match obj:
|
1234
|
+
case Series() as series:
|
1235
|
+
return get_expr_name(series.to_frame(), expr)
|
1236
|
+
case DataFrame() as df:
|
1237
|
+
selected = df.select(expr)
|
1238
|
+
return one(selected.columns)
|
1239
|
+
case _ as never:
|
1240
|
+
assert_never(never)
|
1241
|
+
|
1242
|
+
|
1243
|
+
##
|
1244
|
+
|
1245
|
+
|
1231
1246
|
def get_frequency_spectrum(series: Series, /, *, d: int = 1) -> DataFrame:
|
1232
1247
|
"""Get the frequency spectrum."""
|
1233
1248
|
import utilities.numpy
|
@@ -1970,6 +1985,7 @@ __all__ = [
|
|
1970
1985
|
"finite_ewm_mean",
|
1971
1986
|
"floor_datetime",
|
1972
1987
|
"get_data_type_or_series_time_zone",
|
1988
|
+
"get_expr_name",
|
1973
1989
|
"get_frequency_spectrum",
|
1974
1990
|
"get_series_number_of_decimals",
|
1975
1991
|
"insert_after",
|
utilities/text.py
CHANGED
@@ -4,30 +4,10 @@ import re
|
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from re import IGNORECASE, Match, search
|
6
6
|
from textwrap import dedent
|
7
|
-
from typing import TYPE_CHECKING, Any, override
|
8
|
-
|
9
|
-
from utilities.sentinel import SENTINEL_REPR
|
7
|
+
from typing import TYPE_CHECKING, Any, Literal, overload, override
|
10
8
|
|
11
9
|
if TYPE_CHECKING:
|
12
|
-
from collections.abc import Iterable
|
13
|
-
|
14
|
-
|
15
|
-
def join_strs(
|
16
|
-
texts: Iterable[str],
|
17
|
-
/,
|
18
|
-
*,
|
19
|
-
sort: bool = False,
|
20
|
-
separator: str = ",",
|
21
|
-
empty: str = SENTINEL_REPR,
|
22
|
-
) -> str:
|
23
|
-
"""Join a collection of strings, with a special provision for the empty list."""
|
24
|
-
texts = sorted(texts) if sort else list(texts)
|
25
|
-
if len(texts) >= 1:
|
26
|
-
return separator.join(texts)
|
27
|
-
return empty
|
28
|
-
|
29
|
-
|
30
|
-
##
|
10
|
+
from collections.abc import Iterable, Sequence
|
31
11
|
|
32
12
|
|
33
13
|
def parse_bool(text: str, /) -> bool:
|
@@ -101,11 +81,85 @@ def _snake_case_title(match: Match[str], /) -> str:
|
|
101
81
|
##
|
102
82
|
|
103
83
|
|
84
|
+
def split_key_value_pairs(
|
85
|
+
text: str, /, *, list_separator: str = ",", pair_separator: str = "="
|
86
|
+
) -> Sequence[tuple[str, str]]:
|
87
|
+
"""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
|
+
]
|
92
|
+
|
93
|
+
|
94
|
+
##
|
95
|
+
|
96
|
+
|
97
|
+
@overload
|
98
|
+
def split_str(text: str, /, *, separator: str = ",", n: Literal[1]) -> tuple[str]: ...
|
99
|
+
@overload
|
100
|
+
def split_str(
|
101
|
+
text: str, /, *, separator: str = ",", n: Literal[2]
|
102
|
+
) -> tuple[str, str]: ...
|
103
|
+
@overload
|
104
|
+
def split_str(
|
105
|
+
text: str, /, *, separator: str = ",", n: Literal[3]
|
106
|
+
) -> tuple[str, str, str]: ...
|
107
|
+
@overload
|
108
|
+
def split_str(
|
109
|
+
text: str, /, *, separator: str = ",", n: Literal[4]
|
110
|
+
) -> tuple[str, str, str, str]: ...
|
111
|
+
@overload
|
104
112
|
def split_str(
|
105
|
-
text: str, /, *, separator: str = ",",
|
106
|
-
) ->
|
113
|
+
text: str, /, *, separator: str = ",", n: Literal[5]
|
114
|
+
) -> tuple[str, str, str, str, str]: ...
|
115
|
+
@overload
|
116
|
+
def split_str(
|
117
|
+
text: str, /, *, separator: str = ",", n: int | None = None
|
118
|
+
) -> Sequence[str]: ...
|
119
|
+
def split_str(
|
120
|
+
text: str, /, *, separator: str = ",", n: int | None = None
|
121
|
+
) -> Sequence[str]:
|
107
122
|
"""Split a string, with a special provision for the empty string."""
|
108
|
-
|
123
|
+
if text == "":
|
124
|
+
texts = []
|
125
|
+
elif text == _escape_separator(separator=separator):
|
126
|
+
texts = [""]
|
127
|
+
else:
|
128
|
+
texts = text.split(separator)
|
129
|
+
if n is None:
|
130
|
+
return texts
|
131
|
+
if len(texts) != n:
|
132
|
+
raise SplitStrError(text=text, n=n, texts=texts)
|
133
|
+
return tuple(texts)
|
134
|
+
|
135
|
+
|
136
|
+
@dataclass(kw_only=True, slots=True)
|
137
|
+
class SplitStrError(Exception):
|
138
|
+
text: str
|
139
|
+
n: int
|
140
|
+
texts: Sequence[str]
|
141
|
+
|
142
|
+
@override
|
143
|
+
def __str__(self) -> str:
|
144
|
+
return f"Unable to split {self.text!r} into {self.n} part(s); got {len(self.texts)}"
|
145
|
+
|
146
|
+
|
147
|
+
def join_strs(
|
148
|
+
texts: Iterable[str], /, *, sort: bool = False, separator: str = ","
|
149
|
+
) -> str:
|
150
|
+
"""Join a collection of strings, with a special provision for the empty list."""
|
151
|
+
texts = list(texts)
|
152
|
+
if sort:
|
153
|
+
texts = sorted(texts)
|
154
|
+
if texts == []:
|
155
|
+
return ""
|
156
|
+
if texts == [""]:
|
157
|
+
return _escape_separator(separator=separator)
|
158
|
+
return separator.join(texts)
|
159
|
+
|
160
|
+
|
161
|
+
def _escape_separator(*, separator: str = ",") -> str:
|
162
|
+
return f"\\{separator}"
|
109
163
|
|
110
164
|
|
111
165
|
##
|
@@ -128,11 +182,13 @@ def strip_and_dedent(text: str, /, *, trailing: bool = False) -> str:
|
|
128
182
|
__all__ = [
|
129
183
|
"ParseBoolError",
|
130
184
|
"ParseNoneError",
|
185
|
+
"SplitStrError",
|
131
186
|
"join_strs",
|
132
187
|
"parse_bool",
|
133
188
|
"parse_none",
|
134
189
|
"repr_encode",
|
135
190
|
"snake_case",
|
191
|
+
"split_key_value_pairs",
|
136
192
|
"split_str",
|
137
193
|
"str_encode",
|
138
194
|
"strip_and_dedent",
|
File without changes
|
File without changes
|