dycw-utilities 0.109.7__py3-none-any.whl → 0.109.9__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.7
3
+ Version: 0.109.9
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=MUwL4q6Wbk3lcuTXRAV3j_zzg6Rwbca04bQFKgMtHec,60
1
+ utilities/__init__.py,sha256=53_kiHhGqVZU7uWgI_AWTOmG7yG5Vbb_k-XsqiAMxyk,60
2
2
  utilities/altair.py,sha256=NSyDsm8QlkAGmsGdxVwCkHnPxt_35yJBa9Lg7bz9Ays,9054
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=Rh5QajjwyXGpQqndeOFRT-VnPJXF0jWI515GjCSprS0,23326
14
+ utilities/dataclasses.py,sha256=rQS-G-JwhQNufV80aThSOD_ENliueqkz4N6NqtTTPN8,23294
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
@@ -40,12 +40,12 @@ utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
40
40
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
41
41
  utilities/orjson.py,sha256=Wj5pzG_VdgoAy14a7Luhem-BgYrRtRFvvl_POiszRd0,36930
42
42
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
43
- utilities/parse.py,sha256=hzMDMUA9h3IpgsR48-Dk4CaojXofLm-eAn_bvJ8o6k8,7081
43
+ utilities/parse.py,sha256=D1rqqrULV1FkjnCZS6804Io26_AZbMrsEILf-pZGNzw,7192
44
44
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
45
45
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
46
46
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
47
47
  utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
48
- utilities/polars.py,sha256=USK_Rck8nmFYg2Rs-akqN9jV4w52lpz4rgkWUMQdLMk,49087
48
+ utilities/polars.py,sha256=nB2pfK8N8HRpPE_tdbiTfFGLWC_TekAqgHlYDhnUzAM,52169
49
49
  utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
50
50
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
@@ -53,7 +53,7 @@ utilities/pyinstrument.py,sha256=ROq2txPwbe2ZUuYJ2IDNbfT97lu2ca0v5_C_yn6sSlM,800
53
53
  utilities/pyrsistent.py,sha256=TLJfiiKO4cKNU_pCoM3zDqmSM421qpuoaeaBNnyC_Ac,2489
54
54
  utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
55
55
  utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq3c,5088
56
- utilities/python_dotenv.py,sha256=ZIz45FjpwoMs5fdYr9kRM6f4qEp2GNM5rKLnrRe8_7c,3218
56
+ utilities/python_dotenv.py,sha256=JDIGuaGIiVkOcOBDpA7OZZM_0vxrkZyrrfqOssB6cqE,3170
57
57
  utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
58
58
  utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
59
59
  utilities/redis.py,sha256=CsDQqc9V6ASLzLQwtbQXZQEndyG9pJiCOhPlPeszt7Y,21203
@@ -74,7 +74,7 @@ utilities/text.py,sha256=X_EjRQeV_PsG3oP7OiGYIyXGKWqciTnSwoKhM2tsy6M,3120
74
74
  utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
75
75
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
76
76
  utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
77
- utilities/types.py,sha256=QK8kgH80TJdh_vktaZHrCEk7f1f8kHiDr8dJlK8aSac,17814
77
+ utilities/types.py,sha256=fkaqL67A_1p0-Q88JTu1dt_GV3mVo5mSslYa9DGWNq4,17903
78
78
  utilities/typing.py,sha256=gLg4EbE1FX52fJ1d3ji4i08qolwu9qgWt8w_w_Y5DTk,5512
79
79
  utilities/tzdata.py,sha256=2ZsPmhTVM9Ptrxb4QrWKtKOB9RiH8IOO-A1u7ULdVbg,176
80
80
  utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
@@ -84,7 +84,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
84
84
  utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
85
85
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
86
86
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
87
- dycw_utilities-0.109.7.dist-info/METADATA,sha256=pDfjhbqQ0EwowMUVigqpK60JwGhbUysd3RLQVL398aw,13004
88
- dycw_utilities-0.109.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
- dycw_utilities-0.109.7.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
- dycw_utilities-0.109.7.dist-info/RECORD,,
87
+ dycw_utilities-0.109.9.dist-info/METADATA,sha256=sh80SudoQ_j2lWexCpuls1seZsGjBvS7FlKhOLC6kIE,13004
88
+ dycw_utilities-0.109.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
+ dycw_utilities-0.109.9.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
+ dycw_utilities-0.109.9.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.7"
3
+ __version__ = "0.109.9"
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 TDataclass
27
+ from utilities.types import ParseTextExtra, TDataclass
28
28
  from utilities.typing import get_type_hints
29
29
 
30
30
  if TYPE_CHECKING:
@@ -454,7 +454,7 @@ def text_to_dataclass(
454
454
  head: bool = False,
455
455
  case_sensitive: bool = False,
456
456
  allow_extra_keys: bool = False,
457
- extra_parsers: Mapping[type[_T], Callable[[str], _T]] | None = None,
457
+ extra_parsers: ParseTextExtra | None = None,
458
458
  ) -> TDataclass:
459
459
  """Construct a dataclass from a string or a mapping or strings."""
460
460
  match text_or_mapping:
@@ -524,7 +524,7 @@ def _text_to_dataclass_parse(
524
524
  *,
525
525
  head: bool = False,
526
526
  case_sensitive: bool = False,
527
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
527
+ extra: ParseTextExtra | None = None,
528
528
  ) -> Any:
529
529
  try:
530
530
  return parse_text(
utilities/parse.py CHANGED
@@ -7,7 +7,7 @@ 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 TYPE_CHECKING, Any, TypeVar, override
10
+ from typing import Any, override
11
11
 
12
12
  from utilities.datetime import is_subclass_date_not_datetime
13
13
  from utilities.enum import ParseEnumError, parse_enum
@@ -17,7 +17,7 @@ from utilities.math import ParseNumberError, parse_number
17
17
  from utilities.re import ExtractGroupError, extract_group
18
18
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
19
19
  from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
20
- from utilities.types import Duration, Number
20
+ from utilities.types import Duration, Number, ParseTextExtra
21
21
  from utilities.typing import (
22
22
  get_args,
23
23
  is_literal_type,
@@ -27,12 +27,6 @@ from utilities.typing import (
27
27
  )
28
28
  from utilities.version import ParseVersionError, Version, parse_version
29
29
 
30
- if TYPE_CHECKING:
31
- from collections.abc import Callable, Mapping
32
-
33
-
34
- _T = TypeVar("_T")
35
-
36
30
 
37
31
  def parse_text(
38
32
  obj: Any,
@@ -41,7 +35,7 @@ def parse_text(
41
35
  *,
42
36
  head: bool = False,
43
37
  case_sensitive: bool = False,
44
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
38
+ extra: ParseTextExtra | None = None,
45
39
  ) -> Any:
46
40
  """Parse text."""
47
41
  if obj is None:
@@ -76,7 +70,7 @@ def parse_text(
76
70
  for arg, text in zip(args, texts, strict=True)
77
71
  )
78
72
  if is_union_type(obj):
79
- return _parse_text_union_type(obj, text)
73
+ return _parse_text_union_type(obj, text, extra=extra)
80
74
  raise _ParseTextParseError(obj=obj, text=text) from None
81
75
 
82
76
 
@@ -86,7 +80,7 @@ def _parse_text_type(
86
80
  /,
87
81
  *,
88
82
  case_sensitive: bool = False,
89
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
83
+ extra: ParseTextExtra | None = None,
90
84
  ) -> Any:
91
85
  """Parse text."""
92
86
  if issubclass(cls, NoneType):
@@ -170,7 +164,9 @@ def _parse_text_type(
170
164
  raise _ParseTextParseError(obj=cls, text=text) from None
171
165
 
172
166
 
173
- def _parse_text_union_type(obj: Any, text: str, /) -> Any:
167
+ def _parse_text_union_type(
168
+ obj: Any, text: str, /, *, extra: ParseTextExtra | None = None
169
+ ) -> Any:
174
170
  if obj is Number:
175
171
  try:
176
172
  return parse_number(text)
@@ -183,6 +179,13 @@ def _parse_text_union_type(obj: Any, text: str, /) -> Any:
183
179
  return parse_duration(text)
184
180
  except ParseDurationError:
185
181
  raise _ParseTextParseError(obj=obj, text=text) from None
182
+ if extra is not None:
183
+ try:
184
+ parser = one(p for c, p in extra.items() if c is obj)
185
+ except OneEmptyError:
186
+ pass
187
+ else:
188
+ return parser(text)
186
189
  raise _ParseTextParseError(obj=obj, text=text) from None
187
190
 
188
191
 
utilities/polars.py CHANGED
@@ -1165,6 +1165,52 @@ class _InsertBetweenNonConsecutiveError(InsertBetweenError):
1165
1165
  ##
1166
1166
 
1167
1167
 
1168
+ def integers(
1169
+ obj: int | Series | DataFrame,
1170
+ low: int,
1171
+ /,
1172
+ *,
1173
+ high: int | None = None,
1174
+ seed: int | None = None,
1175
+ endpoint: bool = False,
1176
+ name: str | None = None,
1177
+ dtype: PolarsDataType = Int64,
1178
+ ) -> Series:
1179
+ """Construct a series of normally-distributed numbers."""
1180
+ match obj:
1181
+ case int() as height:
1182
+ from numpy.random import default_rng
1183
+
1184
+ rng = default_rng(seed=seed)
1185
+ values = rng.integers(low, high=high, size=height, endpoint=endpoint)
1186
+ return Series(name=name, values=values, dtype=dtype)
1187
+ case Series() as series:
1188
+ return integers(
1189
+ series.len(),
1190
+ low,
1191
+ high=high,
1192
+ seed=seed,
1193
+ endpoint=endpoint,
1194
+ name=name,
1195
+ dtype=dtype,
1196
+ )
1197
+ case DataFrame() as df:
1198
+ return integers(
1199
+ df.height,
1200
+ low,
1201
+ high=high,
1202
+ seed=seed,
1203
+ endpoint=endpoint,
1204
+ name=name,
1205
+ dtype=dtype,
1206
+ )
1207
+ case _ as never:
1208
+ assert_never(never)
1209
+
1210
+
1211
+ ##
1212
+
1213
+
1168
1214
  def is_not_null_struct_series(series: Series, /) -> Series:
1169
1215
  """Check if a struct-dtype Series is not null as per the <= 1.1 definition."""
1170
1216
  try:
@@ -1324,6 +1370,39 @@ def nan_sum_cols(
1324
1370
  ##
1325
1371
 
1326
1372
 
1373
+ def normal(
1374
+ obj: int | Series | DataFrame,
1375
+ /,
1376
+ *,
1377
+ loc: float = 0.0,
1378
+ scale: float = 1.0,
1379
+ seed: int | None = None,
1380
+ name: str | None = None,
1381
+ dtype: PolarsDataType = Float64,
1382
+ ) -> Series:
1383
+ """Construct a series of normally-distributed numbers."""
1384
+ match obj:
1385
+ case int() as height:
1386
+ from numpy.random import default_rng
1387
+
1388
+ rng = default_rng(seed=seed)
1389
+ values = rng.normal(loc=loc, scale=scale, size=height)
1390
+ return Series(name=name, values=values, dtype=dtype)
1391
+ case Series() as series:
1392
+ return normal(
1393
+ series.len(), loc=loc, scale=scale, seed=seed, name=name, dtype=dtype
1394
+ )
1395
+ case DataFrame() as df:
1396
+ return normal(
1397
+ df.height, loc=loc, scale=scale, seed=seed, name=name, dtype=dtype
1398
+ )
1399
+ case _ as never:
1400
+ assert_never(never)
1401
+
1402
+
1403
+ ##
1404
+
1405
+
1327
1406
  @overload
1328
1407
  def replace_time_zone(
1329
1408
  obj: Series, /, *, time_zone: TimeZoneLike | None = UTC
@@ -1461,6 +1540,39 @@ class _StructFromDataClassTypeError(StructFromDataClassError):
1461
1540
  ##
1462
1541
 
1463
1542
 
1543
+ def uniform(
1544
+ obj: int | Series | DataFrame,
1545
+ /,
1546
+ *,
1547
+ low: float = 0.0,
1548
+ high: float = 1.0,
1549
+ seed: int | None = None,
1550
+ name: str | None = None,
1551
+ dtype: PolarsDataType = Float64,
1552
+ ) -> Series:
1553
+ """Construct a series of uniformly-distributed numbers."""
1554
+ match obj:
1555
+ case int() as height:
1556
+ from numpy.random import default_rng
1557
+
1558
+ rng = default_rng(seed=seed)
1559
+ values = rng.uniform(low=low, high=high, size=height)
1560
+ return Series(name=name, values=values, dtype=dtype)
1561
+ case Series() as series:
1562
+ return uniform(
1563
+ series.len(), low=low, high=high, seed=seed, name=name, dtype=dtype
1564
+ )
1565
+ case DataFrame() as df:
1566
+ return uniform(
1567
+ df.height, low=low, high=high, seed=seed, name=name, dtype=dtype
1568
+ )
1569
+ case _ as never:
1570
+ assert_never(never)
1571
+
1572
+
1573
+ ##
1574
+
1575
+
1464
1576
  def unique_element(column: ExprLike, /) -> Expr:
1465
1577
  """Get the unique element in a list."""
1466
1578
  column = ensure_expr_or_series(column)
@@ -1645,17 +1757,20 @@ __all__ = [
1645
1757
  "insert_after",
1646
1758
  "insert_before",
1647
1759
  "insert_between",
1760
+ "integers",
1648
1761
  "is_not_null_struct_series",
1649
1762
  "is_null_struct_series",
1650
1763
  "join",
1651
1764
  "map_over_columns",
1652
1765
  "nan_sum_agg",
1653
1766
  "nan_sum_cols",
1767
+ "normal",
1654
1768
  "replace_time_zone",
1655
1769
  "set_first_row_as_columns",
1656
1770
  "struct_dtype",
1657
1771
  "struct_from_dataclass",
1658
1772
  "touch",
1773
+ "uniform",
1659
1774
  "unique_element",
1660
1775
  "yield_struct_series_dataclasses",
1661
1776
  "yield_struct_series_elements",
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from os import environ
5
- from typing import TYPE_CHECKING, TypeVar, override
5
+ from typing import TYPE_CHECKING, override
6
6
 
7
7
  from dotenv import dotenv_values
8
8
 
@@ -13,14 +13,11 @@ from utilities.pathlib import PWD
13
13
  from utilities.reprlib import get_repr
14
14
 
15
15
  if TYPE_CHECKING:
16
- from collections.abc import Callable, Mapping
16
+ from collections.abc import Mapping
17
17
  from collections.abc import Set as AbstractSet
18
18
  from pathlib import Path
19
19
 
20
- from utilities.types import PathLike, StrMapping, TDataclass
21
-
22
-
23
- _T = TypeVar("_T")
20
+ from utilities.types import ParseTextExtra, PathLike, StrMapping, TDataclass
24
21
 
25
22
 
26
23
  def load_settings(
@@ -33,7 +30,7 @@ def load_settings(
33
30
  warn_name_errors: bool = False,
34
31
  head: bool = False,
35
32
  case_sensitive: bool = False,
36
- extra_parsers: Mapping[type[_T], Callable[[str], _T]] | None = None,
33
+ extra_parsers: ParseTextExtra | None = None,
37
34
  ) -> TDataclass:
38
35
  """Load a set of settings from the `.env` file."""
39
36
  path = get_repo_root(cwd=cwd).joinpath(".env")
utilities/types.py CHANGED
@@ -229,6 +229,10 @@ class SupportsRound(Protocol[_T_co]):
229
229
  def __round__(self, ndigits: int, /) -> _T_co: ...
230
230
 
231
231
 
232
+ # parse
233
+ type ParseTextExtra = Mapping[Any, Callable[[str], Any]]
234
+
235
+
232
236
  # pathlib
233
237
  type PathLike = MaybeStr[Path]
234
238
  type PathLikeOrCallable = PathLike | Callable[[], PathLike]
@@ -278,6 +282,7 @@ __all__ = [
278
282
  "OpenMode",
279
283
  "OptExcInfo",
280
284
  "Parallelism",
285
+ "ParseTextExtra",
281
286
  "PathLike",
282
287
  "PathLikeOrCallable",
283
288
  "RoundMode",