dycw-utilities 0.109.5__py3-none-any.whl → 0.109.7__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.5
3
+ Version: 0.109.7
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -177,7 +177,7 @@ Requires-Dist: scipy<1.16,>=1.15.2; extra == 'zzz-test-scipy'
177
177
  Provides-Extra: zzz-test-sentinel
178
178
  Provides-Extra: zzz-test-shelve
179
179
  Provides-Extra: zzz-test-slack-sdk
180
- Requires-Dist: aiohttp<3.12,>=3.11.17; extra == 'zzz-test-slack-sdk'
180
+ Requires-Dist: aiohttp<3.12,>=3.11.16; extra == 'zzz-test-slack-sdk'
181
181
  Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
182
182
  Provides-Extra: zzz-test-socket
183
183
  Provides-Extra: zzz-test-sqlalchemy
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=WUcAfLjgcNNeAm9q3alKWOcT4CjZ3liYuA1SFSky9Ng,60
1
+ utilities/__init__.py,sha256=MUwL4q6Wbk3lcuTXRAV3j_zzg6Rwbca04bQFKgMtHec,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
@@ -31,7 +31,7 @@ utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
31
31
  utilities/logging.py,sha256=opIwFjGKOYyMntVeCsFNXOmTY2z02hMf2UtCB76SaI4,25142
32
32
  utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
33
33
  utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
34
- utilities/math.py,sha256=AtUqrZ1Re9rwJjvp3QSlaa9GGIgBMKkslt77YxaNibw,26240
34
+ utilities/math.py,sha256=TexfvLCI12d9Sw5_W4pKVBZ3nRr3zk2iPkcEU7xdEWU,26771
35
35
  utilities/memory_profiler.py,sha256=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
36
36
  utilities/modules.py,sha256=SnhsRHRUS1po_acejrINauihGQpPvVsp8RDNCei1OLQ,3173
37
37
  utilities/more_itertools.py,sha256=CPUxrMAcTwRxbzbhiqPKi3Xx9hxqI0t6gkWjutaibGk,5534
@@ -40,7 +40,7 @@ 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=q1A1-bsXltdAh8We5Acy-lPuVIClWTxsRXQWq5wYyKk,6382
43
+ utilities/parse.py,sha256=hzMDMUA9h3IpgsR48-Dk4CaojXofLm-eAn_bvJ8o6k8,7081
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
@@ -81,10 +81,10 @@ utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
81
81
  utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
82
82
  utilities/version.py,sha256=QFuyEeQA6jI0ruBEcmhqG36f-etg1AEiD1drBBqhQrs,5358
83
83
  utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
84
- utilities/whenever.py,sha256=5x2t47VJmJRWcd_NLFy54NkB3uom-XQYxEbLtEfL1bs,17775
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.5.dist-info/METADATA,sha256=MlQJX_ZNWyT78qMBJR7P1Evz8nzMaBTjTR3PYh2v07A,13004
88
- dycw_utilities-0.109.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
- dycw_utilities-0.109.5.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
- dycw_utilities-0.109.5.dist-info/RECORD,,
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,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.5"
3
+ __version__ = "0.109.7"
utilities/math.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
+ from contextlib import suppress
4
5
  from dataclasses import dataclass
5
6
  from math import ceil, exp, floor, isclose, isfinite, isinf, isnan, log, log10, modf
6
7
  from re import Match, search
@@ -9,7 +10,8 @@ from typing import TYPE_CHECKING, Literal, assert_never, overload, override
9
10
  from utilities.errors import ImpossibleCaseError
10
11
 
11
12
  if TYPE_CHECKING:
12
- from utilities.types import RoundMode
13
+ from utilities.types import Number, RoundMode
14
+
13
15
 
14
16
  MIN_FLOAT32, MAX_FLOAT32 = -3.4028234663852886e38, 3.4028234663852886e38
15
17
  MIN_FLOAT64, MAX_FLOAT64 = -1.7976931348623157e308, 1.7976931348623157e308
@@ -681,6 +683,27 @@ def order_of_magnitude(x: float, /, *, round_: bool = False) -> float:
681
683
  ##
682
684
 
683
685
 
686
+ def parse_number(number: str, /) -> Number:
687
+ """Convert text into a number."""
688
+ with suppress(ValueError):
689
+ return int(number)
690
+ with suppress(ValueError):
691
+ return float(number)
692
+ raise ParseNumberError(number=number)
693
+
694
+
695
+ @dataclass(kw_only=True, slots=True)
696
+ class ParseNumberError(Exception):
697
+ number: str
698
+
699
+ @override
700
+ def __str__(self) -> str:
701
+ return f"Unable to parse number; got {self.number!r}"
702
+
703
+
704
+ ##
705
+
706
+
684
707
  def round_(
685
708
  x: float,
686
709
  /,
@@ -887,6 +910,7 @@ __all__ = [
887
910
  "MIN_UINT64",
888
911
  "CheckIntegerError",
889
912
  "EWMParametersError",
913
+ "ParseNumberError",
890
914
  "SafeRoundError",
891
915
  "check_integer",
892
916
  "ewm_parameters",
@@ -934,6 +958,7 @@ __all__ = [
934
958
  "is_zero_or_non_micro_or_nan",
935
959
  "number_of_decimals",
936
960
  "order_of_magnitude",
961
+ "parse_number",
937
962
  "round_",
938
963
  "round_float_imprecisions",
939
964
  "round_to_float",
utilities/parse.py CHANGED
@@ -13,10 +13,18 @@ from utilities.datetime import is_subclass_date_not_datetime
13
13
  from utilities.enum import ParseEnumError, parse_enum
14
14
  from utilities.functions import is_subclass_int_not_bool
15
15
  from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
16
+ from utilities.math import ParseNumberError, parse_number
16
17
  from utilities.re import ExtractGroupError, extract_group
17
18
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
18
19
  from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
19
- from utilities.typing import get_args, is_literal_type, is_optional_type, is_tuple_type
20
+ from utilities.types import Duration, Number
21
+ from utilities.typing import (
22
+ get_args,
23
+ is_literal_type,
24
+ is_optional_type,
25
+ is_tuple_type,
26
+ is_union_type,
27
+ )
20
28
  from utilities.version import ParseVersionError, Version, parse_version
21
29
 
22
30
  if TYPE_CHECKING:
@@ -31,8 +39,8 @@ def parse_text(
31
39
  text: str,
32
40
  /,
33
41
  *,
34
- case_sensitive: bool = False,
35
42
  head: bool = False,
43
+ case_sensitive: bool = False,
36
44
  extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
37
45
  ) -> Any:
38
46
  """Parse text."""
@@ -49,13 +57,12 @@ def parse_text(
49
57
  with suppress(ParseNoneError):
50
58
  return parse_none(text)
51
59
  inner = one(arg for arg in get_args(obj) if arg is not NoneType)
52
- if isinstance(
53
- inner := one(arg for arg in get_args(obj) if arg is not NoneType), type
54
- ):
55
- try:
56
- return _parse_text_type(inner, text, case_sensitive=case_sensitive)
57
- except _ParseTextParseError:
58
- raise _ParseTextParseError(obj=obj, text=text) from None
60
+ try:
61
+ return parse_text(
62
+ inner, text, head=head, case_sensitive=case_sensitive, extra=extra
63
+ )
64
+ except _ParseTextParseError:
65
+ raise _ParseTextParseError(obj=obj, text=text) from None
59
66
  if is_tuple_type(obj):
60
67
  args = get_args(obj)
61
68
  try:
@@ -65,9 +72,11 @@ def parse_text(
65
72
  if len(args) != len(texts):
66
73
  raise _ParseTextParseError(obj=obj, text=text)
67
74
  return tuple(
68
- parse_text(arg, text, case_sensitive=case_sensitive, head=head)
75
+ parse_text(arg, text, head=head, case_sensitive=case_sensitive, extra=extra)
69
76
  for arg, text in zip(args, texts, strict=True)
70
77
  )
78
+ if is_union_type(obj):
79
+ return _parse_text_union_type(obj, text)
71
80
  raise _ParseTextParseError(obj=obj, text=text) from None
72
81
 
73
82
 
@@ -161,6 +170,22 @@ def _parse_text_type(
161
170
  raise _ParseTextParseError(obj=cls, text=text) from None
162
171
 
163
172
 
173
+ def _parse_text_union_type(obj: Any, text: str, /) -> Any:
174
+ if obj is Number:
175
+ try:
176
+ return parse_number(text)
177
+ except ParseNumberError:
178
+ raise _ParseTextParseError(obj=obj, text=text) from None
179
+ if obj is Duration:
180
+ from utilities.whenever import ParseDurationError, parse_duration
181
+
182
+ try:
183
+ return parse_duration(text)
184
+ except ParseDurationError:
185
+ raise _ParseTextParseError(obj=obj, text=text) from None
186
+ raise _ParseTextParseError(obj=obj, text=text) from None
187
+
188
+
164
189
  @dataclass
165
190
  class ParseTextError(Exception):
166
191
  obj: Any
utilities/whenever.py CHANGED
@@ -17,6 +17,7 @@ from utilities.datetime import (
17
17
  parse_two_digit_year,
18
18
  timedelta_to_microseconds,
19
19
  )
20
+ from utilities.math import ParseNumberError, parse_number
20
21
  from utilities.re import (
21
22
  ExtractGroupError,
22
23
  ExtractGroupsError,
@@ -298,10 +299,8 @@ class ParseDateTimeError(Exception):
298
299
 
299
300
  def parse_duration(duration: str, /) -> Duration:
300
301
  """Parse a string into a Duration."""
301
- with suppress(ValueError):
302
- return int(duration)
303
- with suppress(ValueError):
304
- return float(duration)
302
+ with suppress(ParseNumberError):
303
+ return parse_number(duration)
305
304
  try:
306
305
  return parse_timedelta(duration)
307
306
  except ParseTimedeltaError: