dycw-utilities 0.111.0__py3-none-any.whl → 0.112.0__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.111.0
3
+ Version: 0.112.0
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=fjbCo5KvSkqKWLmPeMS8U77JZlYaYPd9js1ZVmdD1FE,60
1
+ utilities/__init__.py,sha256=Xs6R-k_70_fB8zwc0DeYfdK4J8vyyL3G76ApWiuWPGo,60
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
@@ -12,24 +12,24 @@ 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
14
  utilities/dataclasses.py,sha256=Q197PVnE_vUMn_SNnqJBCo4eRy4bdHtgMHWRbSJPtFk,26670
15
- utilities/datetime.py,sha256=XrVHvyHyYMBeU2ClC7xJKwB68l-F2ZCmDznyNXS4hT8,36862
15
+ utilities/datetime.py,sha256=OF7jZE702UecnwAbq9D3N-GINpp9gSGoidki1RhimCE,35752
16
16
  utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
17
17
  utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
18
18
  utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
19
- utilities/fastapi.py,sha256=uwqOGbGwzIbP-lfm-ApG1ZEN3BA_TDsaiuTghhLmxb8,2413
20
- utilities/fpdf2.py,sha256=zM3gwOYcAfv7P4qhbyvzPmRY4PPAiAQ-ZnPC6I9SZ1M,1832
19
+ utilities/fastapi.py,sha256=y-35at3005jzlNx2wJoiSvB1Ch5bMo30wgU_so3IDdI,2467
20
+ utilities/fpdf2.py,sha256=y1NGXR5chWqLXWpewGV3hlRGMr_5yV1lVRkPBhPEgJI,1843
21
21
  utilities/functions.py,sha256=jgt592voaHNtX56qX0SRvFveVCRmSIxCZmqvpLZCnY8,27305
22
22
  utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
23
23
  utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
24
24
  utilities/git.py,sha256=wpt5dZ5Oi5931pN24_VLZYaQOvmR0OcQuVtgHzFUN1k,2359
25
25
  utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
26
26
  utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
27
- utilities/hypothesis.py,sha256=sLqYcrFn0I3o0R7maqliIERRtAcREk2CKG8rSHY1t5U,46205
27
+ utilities/hypothesis.py,sha256=OpZhPdPmsYWvqMytFDc-G196eODosUzxQSuo-LfMYmM,46262
28
28
  utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
29
29
  utilities/iterables.py,sha256=2Yy9gZ7BR4LXR4nlX7outFAjd4dpb3lgUo7ji_sdylY,45076
30
30
  utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
31
31
  utilities/lightweight_charts.py,sha256=vyVOzarYhBIOZj2xDhqdbP85qbSKUjdc6Au91rc1W4M,2814
32
- utilities/logging.py,sha256=opIwFjGKOYyMntVeCsFNXOmTY2z02hMf2UtCB76SaI4,25142
32
+ utilities/logging.py,sha256=55LVJYd2BKzeTTQr4tG7uSNMDVEgUO56wK4s-rldjrM,25326
33
33
  utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
34
34
  utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
35
35
  utilities/math.py,sha256=TexfvLCI12d9Sw5_W4pKVBZ3nRr3zk2iPkcEU7xdEWU,26771
@@ -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=DBm2zPP04kcHpY3l1etL24ksNynu-R3duFyx3U-RjqQ,36948
43
43
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
44
- utilities/parse.py,sha256=-sDMqA4nB4CFctc86uIlSoKmIUisTUd3qyBcuUnf1D0,18957
44
+ utilities/parse.py,sha256=hG-y8WAzpATakA61UOF6UlhfuJQ9XbMN-Uub1ZaiBRU,18780
45
45
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
46
46
  utilities/period.py,sha256=RWfcNVoNlW07RNdU47g_zuLZMKbtgfK4bE6G-9tVjY8,11024
47
47
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
@@ -51,7 +51,7 @@ 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
53
53
  utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
54
- utilities/pyinstrument.py,sha256=ROq2txPwbe2ZUuYJ2IDNbfT97lu2ca0v5_C_yn6sSlM,800
54
+ utilities/pyinstrument.py,sha256=OJFDh4o1CWIa4aYPYURdQjgap_nvP45KUsCEe94rQHY,829
55
55
  utilities/pyrsistent.py,sha256=MoDcAqQGlSNkmlS32DCJLw-cZFAfHB6K9kpox_iyI4k,2512
56
56
  utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
57
57
  utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq3c,5088
@@ -76,18 +76,18 @@ utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
76
76
  utilities/text.py,sha256=hfcBKF22fKT6s_U-ZdP-g5TjFQ0-NrIrQdvIwERWT80,10971
77
77
  utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
78
78
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
79
- utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
79
+ utilities/traceback.py,sha256=secexUnBsecfWV4ZuqP1W4pGF3prOeO1CRyJK-8zQDU,27402
80
80
  utilities/types.py,sha256=kVY71hZkcnyYNIlYSse0mLm8yeP3OBkzhDPMME6jXxo,18126
81
81
  utilities/typing.py,sha256=jtc6EiGZGG0E745jo3NeLqo_HdHt7Zdaco3kCAEWIYU,11177
82
- utilities/tzdata.py,sha256=2ZsPmhTVM9Ptrxb4QrWKtKOB9RiH8IOO-A1u7ULdVbg,176
83
- utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
82
+ utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
83
+ utilities/tzlocal.py,sha256=3upDNFBvGh1l9njmLR2z2S6K6VxQSb7QizYGUbAH3JU,960
84
84
  utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
85
85
  utilities/version.py,sha256=QFuyEeQA6jI0ruBEcmhqG36f-etg1AEiD1drBBqhQrs,5358
86
86
  utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
87
- utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
87
+ utilities/whenever.py,sha256=iLRP_-8CZtBpHKbGZGu-kjSMg1ZubJ-VSmgSy7Eudxw,17787
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
- utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
90
- dycw_utilities-0.111.0.dist-info/METADATA,sha256=9UDVxMivd6ye3O57NEI2wEddqVpW9OE_rc_uhtMXD-c,13004
91
- dycw_utilities-0.111.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.111.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.111.0.dist-info/RECORD,,
89
+ utilities/zoneinfo.py,sha256=-Xm57PMMwDTYpxJdkiJG13wnbwK--I7XItBh5WVhD-o,1874
90
+ dycw_utilities-0.112.0.dist-info/METADATA,sha256=GWKYE-rIVvmMBNyTnfCxMJNqV6PHKYsmLVs-6YJadKw,13004
91
+ dycw_utilities-0.112.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.112.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.112.0.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.111.0"
3
+ __version__ = "0.112.0"
utilities/datetime.py CHANGED
@@ -23,13 +23,7 @@ from utilities.platform import SYSTEM
23
23
  from utilities.sentinel import Sentinel, sentinel
24
24
  from utilities.types import MaybeStr
25
25
  from utilities.typing import is_instance_gen
26
- from utilities.zoneinfo import (
27
- UTC,
28
- HongKong,
29
- Tokyo,
30
- ensure_time_zone,
31
- get_time_zone_name,
32
- )
26
+ from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
33
27
 
34
28
  if TYPE_CHECKING:
35
29
  from collections.abc import Iterator
@@ -525,28 +519,6 @@ def get_now(*, time_zone: TimeZoneLike = UTC) -> dt.datetime:
525
519
  NOW_UTC = get_now(time_zone=UTC)
526
520
 
527
521
 
528
- def get_now_hk() -> dt.datetime:
529
- """Get the current time in Hong Kong."""
530
- return dt.datetime.now(tz=HongKong)
531
-
532
-
533
- NOW_HK = get_now_hk()
534
-
535
-
536
- def get_now_local() -> dt.datetime:
537
- """Get the current time in local."""
538
- return get_now(time_zone="local")
539
- # don't define `NOW_LOCAL` as this would require `tzlocal`
540
-
541
-
542
- def get_now_tokyo() -> dt.datetime:
543
- """Get the current time in Tokyo."""
544
- return dt.datetime.now(tz=Tokyo)
545
-
546
-
547
- NOW_TOKYO = get_now_tokyo()
548
-
549
-
550
522
  ##
551
523
 
552
524
 
@@ -570,27 +542,6 @@ def get_today(*, time_zone: TimeZoneLike = UTC) -> dt.date:
570
542
  TODAY_UTC = get_today(time_zone=UTC)
571
543
 
572
544
 
573
- def get_today_hk() -> dt.date:
574
- """Get the current date in Hong Kong."""
575
- return get_now_hk().date()
576
-
577
-
578
- TODAY_HK = get_today_hk()
579
-
580
-
581
- def get_today_local() -> dt.date:
582
- """Get the current, timezone-aware local date."""
583
- return get_now_local().date()
584
-
585
-
586
- def get_today_tokyo() -> dt.date:
587
- """Get the current date in Tokyo."""
588
- return get_now_tokyo().date()
589
-
590
-
591
- TODAY_TOKYO = get_today_tokyo()
592
-
593
-
594
545
  ##
595
546
 
596
547
 
@@ -1279,13 +1230,9 @@ __all__ = [
1279
1230
  "MIN_DATE_TWO_DIGIT_YEAR",
1280
1231
  "MIN_MONTH",
1281
1232
  "MONTH",
1282
- "NOW_HK",
1283
- "NOW_TOKYO",
1284
1233
  "NOW_UTC",
1285
1234
  "QUARTER",
1286
1235
  "SECOND",
1287
- "TODAY_HK",
1288
- "TODAY_TOKYO",
1289
1236
  "TODAY_UTC",
1290
1237
  "WEEK",
1291
1238
  "YEAR",
@@ -1335,14 +1282,8 @@ __all__ = [
1335
1282
  "get_half_years",
1336
1283
  "get_months",
1337
1284
  "get_now",
1338
- "get_now_hk",
1339
- "get_now_local",
1340
- "get_now_tokyo",
1341
1285
  "get_quarters",
1342
1286
  "get_today",
1343
- "get_today_hk",
1344
- "get_today_local",
1345
- "get_today_tokyo",
1346
1287
  "get_years",
1347
1288
  "is_integral_timedelta",
1348
1289
  "is_local_datetime",
utilities/fastapi.py CHANGED
@@ -7,7 +7,7 @@ from fastapi import FastAPI
7
7
  from uvicorn import Config, Server
8
8
 
9
9
  from utilities.asyncio import AsyncService
10
- from utilities.datetime import SECOND, datetime_duration_to_float, get_now_local
10
+ from utilities.datetime import SECOND, datetime_duration_to_float
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from utilities.types import Duration
@@ -26,6 +26,7 @@ class _PingerReceiverApp(FastAPI):
26
26
 
27
27
  @self.get("/ping") # skipif-ci
28
28
  def ping() -> str:
29
+ from utilities.tzlocal import get_now_local # skipif-ci
29
30
  from utilities.whenever import serialize_zoned_datetime # skipif-ci
30
31
 
31
32
  now = serialize_zoned_datetime(get_now_local()) # skipif-ci
utilities/fpdf2.py CHANGED
@@ -6,8 +6,6 @@ from typing import TYPE_CHECKING, override
6
6
  from fpdf import FPDF
7
7
  from fpdf.enums import XPos, YPos
8
8
 
9
- from utilities.datetime import get_now_local
10
-
11
9
  if TYPE_CHECKING:
12
10
  from collections.abc import Iterator
13
11
 
@@ -45,6 +43,8 @@ def yield_pdf(*, header: str | None = None) -> Iterator[_BasePDF]:
45
43
 
46
44
  @override
47
45
  def footer(self) -> None:
46
+ from utilities.tzlocal import get_now_local
47
+
48
48
  self.set_y(-15)
49
49
  self.set_font(family="Helvetica", style="I", size=8)
50
50
  page_no, now = self.page_no(), get_now_local()
utilities/hypothesis.py CHANGED
@@ -69,7 +69,6 @@ from utilities.datetime import (
69
69
  date_to_month,
70
70
  datetime_duration_to_float,
71
71
  datetime_duration_to_timedelta,
72
- get_now_local,
73
72
  round_datetime,
74
73
  )
75
74
  from utilities.functions import ensure_int, ensure_str, max_nullable, min_nullable
@@ -1440,6 +1439,7 @@ def yield_test_redis(data: DataObject, /) -> AbstractAsyncContextManager[_TestRe
1440
1439
  from redis.exceptions import ResponseError # skipif-ci-and-not-linux
1441
1440
 
1442
1441
  from utilities.redis import _TestRedis, yield_redis # skipif-ci-and-not-linux
1442
+ from utilities.tzlocal import get_now_local # skipif-ci-and-not-linux
1443
1443
 
1444
1444
  now = get_now_local() # skipif-ci-and-not-linux
1445
1445
  uuid = data.draw(uuids()) # skipif-ci-and-not-linux
utilities/logging.py CHANGED
@@ -37,11 +37,9 @@ from typing import (
37
37
  override,
38
38
  )
39
39
 
40
- from utilities.atomicwrites import move_many, writer
41
40
  from utilities.dataclasses import replace_non_sentinel
42
41
  from utilities.datetime import (
43
42
  SECOND,
44
- get_now_local,
45
43
  maybe_sub_pct_y,
46
44
  parse_datetime_compact,
47
45
  round_datetime,
@@ -200,6 +198,8 @@ def _compute_rollover_actions(
200
198
  patterns: _RolloverPatterns | None = None,
201
199
  backup_count: int = 1,
202
200
  ) -> _RolloverActions:
201
+ from utilities.tzlocal import get_now_local # skipif-ci-and-windows
202
+
203
203
  patterns = ( # skipif-ci-and-windows
204
204
  _compute_rollover_patterns(stem, suffix) if patterns is None else patterns
205
205
  )
@@ -244,6 +244,8 @@ class _RolloverActions:
244
244
  rotations: set[_Rotation] = field(default_factory=set)
245
245
 
246
246
  def do(self) -> None:
247
+ from utilities.atomicwrites import move_many # skipif-ci-and-windows
248
+
247
249
  for deletion in self.deletions: # skipif-ci-and-windows
248
250
  deletion.delete()
249
251
  move_many( # skipif-ci-and-windows
@@ -388,6 +390,9 @@ class StandaloneFileHandler(Handler):
388
390
 
389
391
  @override
390
392
  def emit(self, record: LogRecord) -> None:
393
+ from utilities.atomicwrites import writer
394
+ from utilities.tzlocal import get_now_local
395
+
391
396
  try:
392
397
  path = (
393
398
  resolve_path(path=self._path)
utilities/parse.py CHANGED
@@ -60,15 +60,15 @@ def parse_object(
60
60
  extra: ParseObjectExtra | None = None,
61
61
  ) -> Any:
62
62
  """Parse text."""
63
+ if extra is not None:
64
+ return _parse_object_extra(type_, text, extra)
63
65
  if type_ is None:
64
66
  try:
65
67
  return parse_none(text)
66
68
  except ParseNoneError:
67
69
  raise _ParseObjectParseError(type_=type_, text=text) from None
68
70
  if isinstance(type_, type):
69
- return _parse_object_type(
70
- type_, text, case_sensitive=case_sensitive, extra=extra
71
- )
71
+ return _parse_object_type(type_, text, case_sensitive=case_sensitive)
72
72
  if is_dict_type(type_):
73
73
  return _parse_object_dict_type(
74
74
  type_,
@@ -146,17 +146,12 @@ def parse_object(
146
146
  extra=extra,
147
147
  )
148
148
  if is_union_type(type_):
149
- return _parse_object_union_type(type_, text, extra=extra)
149
+ return _parse_object_union_type(type_, text)
150
150
  raise _ParseObjectParseError(type_=type_, text=text) from None
151
151
 
152
152
 
153
153
  def _parse_object_type(
154
- cls: type[Any],
155
- text: str,
156
- /,
157
- *,
158
- case_sensitive: bool = False,
159
- extra: ParseObjectExtra | None = None,
154
+ cls: type[Any], text: str, /, *, case_sensitive: bool = False
160
155
  ) -> Any:
161
156
  """Parse text."""
162
157
  if issubclass(cls, NoneType):
@@ -226,17 +221,6 @@ def _parse_object_type(
226
221
  return parse_timedelta(text)
227
222
  except ParseTimedeltaError:
228
223
  raise _ParseObjectParseError(type_=cls, text=text) from None
229
- if extra is not None:
230
- try:
231
- parser = one(p for c, p in extra.items() if issubclass(cls, c))
232
- except OneEmptyError:
233
- pass
234
- except OneNonUniqueError as error:
235
- raise _ParseObjectExtraNonUniqueError(
236
- type_=cls, text=text, first=error.first, second=error.second
237
- ) from None
238
- else:
239
- return parser(text)
240
224
  raise _ParseObjectParseError(type_=cls, text=text)
241
225
 
242
226
 
@@ -296,6 +280,21 @@ def _parse_object_dict_type(
296
280
  raise _ParseObjectParseError(type_=type_, text=text) from None
297
281
 
298
282
 
283
+ def _parse_object_extra(cls: Any, text: str, extra: ParseObjectExtra, /) -> Any:
284
+ try:
285
+ parser = one(
286
+ p for c, p in extra.items() if (cls is c) or is_subclass_gen(cls, c)
287
+ )
288
+ except OneEmptyError:
289
+ raise _ParseObjectParseError(type_=cls, text=text) from None
290
+ except OneNonUniqueError as error:
291
+ raise _ParseObjectExtraNonUniqueError(
292
+ type_=cls, text=text, first=error.first, second=error.second
293
+ ) from None
294
+ else:
295
+ return parser(text)
296
+
297
+
299
298
  def _parse_object_list_type(
300
299
  type_: Any,
301
300
  text: str,
@@ -368,9 +367,7 @@ def _parse_object_set_type(
368
367
  raise _ParseObjectParseError(type_=type_, text=text) from None
369
368
 
370
369
 
371
- def _parse_object_union_type(
372
- type_: Any, text: str, /, *, extra: ParseObjectExtra | None = None
373
- ) -> Any:
370
+ def _parse_object_union_type(type_: Any, text: str, /) -> Any:
374
371
  if type_ is Number:
375
372
  try:
376
373
  return parse_number(text)
@@ -383,13 +380,6 @@ def _parse_object_union_type(
383
380
  return parse_duration(text)
384
381
  except ParseDurationError:
385
382
  raise _ParseObjectParseError(type_=type_, text=text) from None
386
- if extra is not None:
387
- try:
388
- parser = one(p for c, p in extra.items() if c is type_)
389
- except OneEmptyError:
390
- pass
391
- else:
392
- return parser(text)
393
383
  raise _ParseObjectParseError(type_=type_, text=text) from None
394
384
 
395
385
 
@@ -466,6 +456,9 @@ def serialize_object(
466
456
  extra: SerializeObjectExtra | None = None,
467
457
  ) -> str:
468
458
  """Convert an object to text."""
459
+ if extra is not None:
460
+ with suppress(_SerializeObjectSerializeError):
461
+ return _serialize_object_extra(obj, extra)
469
462
  if (obj is None) or isinstance(
470
463
  obj, bool | int | float | str | Path | Sentinel | Version
471
464
  ):
@@ -504,8 +497,6 @@ def serialize_object(
504
497
  return _serialize_object_set(
505
498
  obj, list_separator=list_separator, pair_separator=pair_separator
506
499
  )
507
- if extra is not None:
508
- return _serialize_object_extra(obj, extra)
509
500
  raise _SerializeObjectSerializeError(obj=obj)
510
501
 
511
502
 
@@ -537,10 +528,7 @@ def _serialize_object_dict(
537
528
  def _serialize_object_extra(obj: Any, extra: SerializeObjectExtra, /) -> str:
538
529
  try:
539
530
  serializer = one(
540
- s
541
- for c, s in extra.items()
542
- if (isinstance(c, type) and isinstance(obj, c))
543
- or isinstance(obj, get_args(c))
531
+ s for c, s in extra.items() if (obj is c) or is_instance_gen(obj, c)
544
532
  )
545
533
  except OneEmptyError:
546
534
  raise _SerializeObjectSerializeError(obj=obj) from None
utilities/pyinstrument.py CHANGED
@@ -6,8 +6,9 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  from pyinstrument.profiler import Profiler
8
8
 
9
- from utilities.datetime import get_now_local, serialize_compact
9
+ from utilities.datetime import serialize_compact
10
10
  from utilities.pathlib import PWD
11
+ from utilities.tzlocal import get_now_local
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from collections.abc import Iterator
utilities/traceback.py CHANGED
@@ -26,7 +26,6 @@ from typing import (
26
26
  runtime_checkable,
27
27
  )
28
28
 
29
- from utilities.datetime import get_now_local
30
29
  from utilities.errors import ImpossibleCaseError
31
30
  from utilities.functions import (
32
31
  ensure_not_none,
@@ -44,10 +43,8 @@ from utilities.reprlib import (
44
43
  RICH_MAX_STRING,
45
44
  RICH_MAX_WIDTH,
46
45
  )
47
- from utilities.rich import yield_call_args_repr, yield_mapping_repr
48
46
  from utilities.types import TBaseException, TCallable
49
47
  from utilities.version import get_version
50
- from utilities.whenever import serialize_zoned_datetime
51
48
 
52
49
  if TYPE_CHECKING:
53
50
  from collections.abc import Callable, Iterable, Iterator
@@ -192,6 +189,8 @@ class _CallArgs:
192
189
  @classmethod
193
190
  def create(cls, func: Callable[..., Any], *args: Any, **kwargs: Any) -> Self:
194
191
  """Make the initial trace data."""
192
+ from utilities.rich import yield_call_args_repr
193
+
195
194
  sig = signature(func)
196
195
  try:
197
196
  bound_args = sig.bind(*args, **kwargs)
@@ -428,6 +427,8 @@ class _Frame:
428
427
  depth: int = 0,
429
428
  ) -> str:
430
429
  """Format the traceback."""
430
+ from utilities.rich import yield_call_args_repr, yield_mapping_repr
431
+
431
432
  lines: list[str] = [f"Frame {index + 1}/{total}: {self.name} ({self.module})"]
432
433
  if detail:
433
434
  lines.append(indent("Inputs:", _INDENT))
@@ -784,6 +785,9 @@ def _yield_header_lines(
784
785
  *, version: MaybeCallableVersionLike | None = None
785
786
  ) -> Iterator[str]:
786
787
  """Yield the header lines."""
788
+ from utilities.tzlocal import get_now_local
789
+ from utilities.whenever import serialize_zoned_datetime
790
+
787
791
  yield f"Date/time | {serialize_zoned_datetime(get_now_local())}"
788
792
  yield f"User | {getuser()}"
789
793
  yield f"Host | {gethostname()}"
utilities/tzdata.py CHANGED
@@ -1,9 +1,63 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TYPE_CHECKING
3
4
  from zoneinfo import ZoneInfo
4
5
 
6
+ from utilities.datetime import get_now, get_today
7
+
8
+ if TYPE_CHECKING:
9
+ import datetime as dt
10
+
11
+
12
+ HongKong = ZoneInfo("Asia/Hong_Kong")
13
+ Tokyo = ZoneInfo("Asia/Tokyo")
5
14
  USCentral = ZoneInfo("US/Central")
6
15
  USEastern = ZoneInfo("US/Eastern")
7
16
 
8
17
 
9
- __all__ = ["USCentral", "USEastern"]
18
+ def get_now_hong_kong() -> dt.datetime:
19
+ """Get the current time in Hong Kong."""
20
+ return get_now(time_zone=HongKong)
21
+
22
+
23
+ NOW_HONG_KONG = get_now_hong_kong()
24
+
25
+
26
+ def get_now_tokyo() -> dt.datetime:
27
+ """Get the current time in Tokyo."""
28
+ return get_now(time_zone=Tokyo)
29
+
30
+
31
+ NOW_TOKYO = get_now_tokyo()
32
+
33
+
34
+ def get_today_hong_kong() -> dt.date:
35
+ """Get the current date in Hong Kong."""
36
+ return get_today(time_zone=HongKong)
37
+
38
+
39
+ TODAY_HONG_KONG = get_today_hong_kong()
40
+
41
+
42
+ def get_today_tokyo() -> dt.date:
43
+ """Get the current date in Tokyo."""
44
+ return get_today(time_zone=Tokyo)
45
+
46
+
47
+ TODAY_TOKYO = get_today_tokyo()
48
+
49
+
50
+ __all__ = [
51
+ "NOW_HONG_KONG",
52
+ "NOW_TOKYO",
53
+ "TODAY_HONG_KONG",
54
+ "TODAY_TOKYO",
55
+ "HongKong",
56
+ "Tokyo",
57
+ "USCentral",
58
+ "USEastern",
59
+ "get_now_hong_kong",
60
+ "get_now_tokyo",
61
+ "get_today_hong_kong",
62
+ "get_today_tokyo",
63
+ ]
utilities/tzlocal.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import datetime as dt
3
4
  from logging import getLogger
4
5
  from typing import TYPE_CHECKING
5
6
 
@@ -19,4 +20,26 @@ def get_local_time_zone() -> ZoneInfo:
19
20
  return time_zone
20
21
 
21
22
 
22
- __all__ = ["get_local_time_zone"]
23
+ def get_now_local() -> dt.datetime:
24
+ """Get the current local time."""
25
+ return dt.datetime.now(tz=get_local_time_zone())
26
+
27
+
28
+ NOW_LOCAL = get_now_local()
29
+
30
+
31
+ def get_today_local() -> dt.date:
32
+ """Get the current, timezone-aware local date."""
33
+ return get_now_local().date()
34
+
35
+
36
+ TODAY_LOCAL = get_today_local()
37
+
38
+
39
+ __all__ = [
40
+ "NOW_LOCAL",
41
+ "TODAY_LOCAL",
42
+ "get_local_time_zone",
43
+ "get_now_local",
44
+ "get_today_local",
45
+ ]
utilities/whenever.py CHANGED
@@ -36,6 +36,7 @@ if TYPE_CHECKING:
36
36
  TimeLike,
37
37
  )
38
38
 
39
+
39
40
  MAX_SERIALIZABLE_TIMEDELTA = dt.timedelta(days=3659635, microseconds=-1)
40
41
  MIN_SERIALIZABLE_TIMEDELTA = -MAX_SERIALIZABLE_TIMEDELTA
41
42
 
utilities/zoneinfo.py CHANGED
@@ -8,10 +8,10 @@ from zoneinfo import ZoneInfo
8
8
  if TYPE_CHECKING:
9
9
  from utilities.types import TimeZone, TimeZoneLike
10
10
 
11
- HongKong = ZoneInfo("Asia/Hong_Kong")
12
- Tokyo = ZoneInfo("Asia/Tokyo")
11
+
13
12
  UTC = ZoneInfo("UTC")
14
13
 
14
+
15
15
  ##
16
16
 
17
17
 
@@ -68,11 +68,4 @@ def get_time_zone_name(time_zone: TimeZoneLike, /) -> TimeZone:
68
68
  return cast("TimeZone", ensure_time_zone(time_zone).key)
69
69
 
70
70
 
71
- __all__ = [
72
- "UTC",
73
- "EnsureTimeZoneError",
74
- "HongKong",
75
- "Tokyo",
76
- "ensure_time_zone",
77
- "get_time_zone_name",
78
- ]
71
+ __all__ = ["UTC", "EnsureTimeZoneError", "ensure_time_zone", "get_time_zone_name"]