dycw-utilities 0.129.6__py3-none-any.whl → 0.129.8__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.129.6.dist-info → dycw_utilities-0.129.8.dist-info}/METADATA +3 -4
- {dycw_utilities-0.129.6.dist-info → dycw_utilities-0.129.8.dist-info}/RECORD +8 -8
- utilities/__init__.py +1 -1
- utilities/logging.py +46 -18
- utilities/traceback.py +39 -8
- utilities/whenever.py +64 -1
- {dycw_utilities-0.129.6.dist-info → dycw_utilities-0.129.8.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.129.6.dist-info → dycw_utilities-0.129.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.129.
|
3
|
+
Version: 0.129.8
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
@@ -93,12 +93,11 @@ Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-iterables'
|
|
93
93
|
Requires-Dist: whenever<0.9,>=0.8.4; extra == 'zzz-test-iterables'
|
94
94
|
Provides-Extra: zzz-test-jupyter
|
95
95
|
Requires-Dist: jupyterlab<4.3,>=4.2.0; extra == 'zzz-test-jupyter'
|
96
|
-
Requires-Dist: pandas<2.
|
96
|
+
Requires-Dist: pandas<2.4,>=2.3.0; extra == 'zzz-test-jupyter'
|
97
97
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-jupyter'
|
98
98
|
Provides-Extra: zzz-test-logging
|
99
99
|
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-logging'
|
100
100
|
Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'zzz-test-logging'
|
101
|
-
Requires-Dist: concurrent-log-handler<0.10,>=0.9.26; extra == 'zzz-test-logging'
|
102
101
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-logging'
|
103
102
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-logging'
|
104
103
|
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-logging'
|
@@ -173,7 +172,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
|
|
173
172
|
Provides-Extra: zzz-test-sentinel
|
174
173
|
Provides-Extra: zzz-test-shelve
|
175
174
|
Provides-Extra: zzz-test-slack-sdk
|
176
|
-
Requires-Dist: aiohttp<3.12.
|
175
|
+
Requires-Dist: aiohttp<3.12.10,>=3.12.9; extra == 'zzz-test-slack-sdk'
|
177
176
|
Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
|
178
177
|
Provides-Extra: zzz-test-socket
|
179
178
|
Provides-Extra: zzz-test-sqlalchemy
|
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=nud8TbHJzm02w0ycVV5vCV-F8CpfmCs3mB9e6p4DrdQ,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/asyncio.py,sha256=3n5EIcSq2xtEF1i4oR0oY2JmBq3NyugeHKFK39Mt22s,37987
|
4
4
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
@@ -29,7 +29,7 @@ utilities/iterables.py,sha256=mDqw2_0MUVp-P8FklgcaVTi2TXduH0MxbhTDzzhSBho,44915
|
|
29
29
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
30
30
|
utilities/libcst.py,sha256=Jto5ppzRzsxn4AD32IS8n0lbgLYXwsVJB6EY8giNZyY,4974
|
31
31
|
utilities/lightweight_charts.py,sha256=0xNfcsrgFI0R9xL25LtSm-W5yhfBI93qQNT6HyaXAhg,2769
|
32
|
-
utilities/logging.py,sha256=
|
32
|
+
utilities/logging.py,sha256=dA54i2gmULBLuEJ2roGYWt9pW2NvNBmx0YxlMns347M,26126
|
33
33
|
utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
|
34
34
|
utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
|
35
35
|
utilities/math.py,sha256=-mQgbah-dPJwOEWf3SonrFoVZ2AVxMgpeQ3dfVa-oJA,26764
|
@@ -78,7 +78,7 @@ utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
|
|
78
78
|
utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
|
79
79
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
80
80
|
utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
|
81
|
-
utilities/traceback.py,sha256=
|
81
|
+
utilities/traceback.py,sha256=Rf_4XIz6AQaBcTRr7Tiw7RCIv_O_bN7Hd-Cnr8SPXN4,28920
|
82
82
|
utilities/types.py,sha256=gP04CcCOyFrG7BgblVCsrrChiuO2x842NDVW-GF7odo,18370
|
83
83
|
utilities/typing.py,sha256=H6ysJkI830aRwLsMKz0SZIw4cpcsm7d6KhQOwr-SDh0,13817
|
84
84
|
utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
|
@@ -86,10 +86,10 @@ utilities/tzlocal.py,sha256=3upDNFBvGh1l9njmLR2z2S6K6VxQSb7QizYGUbAH3JU,960
|
|
86
86
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
87
87
|
utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
|
88
88
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
89
|
-
utilities/whenever.py,sha256=
|
89
|
+
utilities/whenever.py,sha256=QbXgFAPuUL7PCp2hajmIP-FFIfIR1J6Y0TxJbeoj60I,18434
|
90
90
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
91
91
|
utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
|
92
|
-
dycw_utilities-0.129.
|
93
|
-
dycw_utilities-0.129.
|
94
|
-
dycw_utilities-0.129.
|
95
|
-
dycw_utilities-0.129.
|
92
|
+
dycw_utilities-0.129.8.dist-info/METADATA,sha256=JaBYjbXepIOBDjYciDSC1_bSCE8VlzxhpOPAs22-nMI,12723
|
93
|
+
dycw_utilities-0.129.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
94
|
+
dycw_utilities-0.129.8.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
95
|
+
dycw_utilities-0.129.8.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/logging.py
CHANGED
@@ -107,8 +107,9 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
107
107
|
utc: bool = False,
|
108
108
|
atTime: dt.time | None = None,
|
109
109
|
) -> None:
|
110
|
-
|
111
|
-
|
110
|
+
path = Path(filename)
|
111
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
112
|
+
super().__init__(path, mode, encoding=encoding, delay=delay, errors=errors)
|
112
113
|
self._max_bytes = maxBytes if maxBytes >= 1 else None
|
113
114
|
self._backup_count = backupCount if backupCount >= 1 else None
|
114
115
|
self._filename = Path(self.baseFilename)
|
@@ -117,7 +118,7 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
117
118
|
self._suffix = self._filename.suffix
|
118
119
|
self._patterns = _compute_rollover_patterns(self._stem, self._suffix)
|
119
120
|
self._time_handler = TimedRotatingFileHandler(
|
120
|
-
|
121
|
+
path,
|
121
122
|
when=when,
|
122
123
|
interval=interval,
|
123
124
|
backupCount=backupCount,
|
@@ -415,26 +416,53 @@ def add_filters(handler: Handler, /, *filters: _FilterType) -> None:
|
|
415
416
|
|
416
417
|
def basic_config(
|
417
418
|
*,
|
418
|
-
|
419
|
+
obj: LoggerOrName | Handler | None = None,
|
419
420
|
format_: str = "{asctime} | {name} | {levelname:8} | {message}",
|
421
|
+
whenever: bool = False,
|
420
422
|
level: LogLevel = "INFO",
|
423
|
+
plain: bool = False,
|
421
424
|
) -> None:
|
422
425
|
"""Do the basic config."""
|
426
|
+
if whenever:
|
427
|
+
format_ = format_.replace("{asctime}", "{zoned_datetime}")
|
423
428
|
datefmt = maybe_sub_pct_y("%Y-%m-%d %H:%M:%S")
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
429
|
+
match obj:
|
430
|
+
case None:
|
431
|
+
basicConfig(format=format_, datefmt=datefmt, style="{", level=level)
|
432
|
+
case Logger() as logger:
|
433
|
+
logger.setLevel(level)
|
434
|
+
logger.addHandler(handler := StreamHandler())
|
435
|
+
basic_config(
|
436
|
+
obj=handler,
|
437
|
+
format_=format_,
|
438
|
+
whenever=whenever,
|
439
|
+
level=level,
|
440
|
+
plain=plain,
|
441
|
+
)
|
442
|
+
case str() as name:
|
443
|
+
basic_config(
|
444
|
+
obj=get_logger(logger=name),
|
445
|
+
format_=format_,
|
446
|
+
whenever=whenever,
|
447
|
+
level=level,
|
448
|
+
plain=plain,
|
449
|
+
)
|
450
|
+
case Handler() as handler:
|
451
|
+
handler.setLevel(level)
|
452
|
+
if plain:
|
453
|
+
formatter = Formatter(fmt=format_, datefmt=datefmt, style="{")
|
454
|
+
else:
|
455
|
+
try:
|
456
|
+
from coloredlogs import ColoredFormatter
|
457
|
+
except ModuleNotFoundError: # pragma: no cover
|
458
|
+
formatter = Formatter(fmt=format_, datefmt=datefmt, style="{")
|
459
|
+
else:
|
460
|
+
formatter = ColoredFormatter(
|
461
|
+
fmt=format_, datefmt=datefmt, style="{"
|
462
|
+
)
|
463
|
+
handler.setFormatter(formatter)
|
464
|
+
case _ as never:
|
465
|
+
assert_never(never)
|
438
466
|
|
439
467
|
|
440
468
|
##
|
utilities/traceback.py
CHANGED
@@ -26,6 +26,7 @@ from typing import (
|
|
26
26
|
runtime_checkable,
|
27
27
|
)
|
28
28
|
|
29
|
+
from utilities.datetime import get_datetime, get_now
|
29
30
|
from utilities.errors import ImpossibleCaseError
|
30
31
|
from utilities.functions import (
|
31
32
|
ensure_not_none,
|
@@ -45,8 +46,9 @@ from utilities.reprlib import (
|
|
45
46
|
yield_call_args_repr,
|
46
47
|
yield_mapping_repr,
|
47
48
|
)
|
48
|
-
from utilities.types import TBaseException, TCallable
|
49
|
+
from utilities.types import MaybeCallableDateTime, TBaseException, TCallable
|
49
50
|
from utilities.version import get_version
|
51
|
+
from utilities.whenever import serialize_duration
|
50
52
|
|
51
53
|
if TYPE_CHECKING:
|
52
54
|
from collections.abc import Callable, Iterable, Iterator
|
@@ -60,6 +62,7 @@ if TYPE_CHECKING:
|
|
60
62
|
_T = TypeVar("_T")
|
61
63
|
_CALL_ARGS = "_CALL_ARGS"
|
62
64
|
_INDENT = 4 * " "
|
65
|
+
_START = get_now()
|
63
66
|
|
64
67
|
|
65
68
|
##
|
@@ -78,6 +81,7 @@ class RichTracebackFormatter(Formatter):
|
|
78
81
|
/,
|
79
82
|
*,
|
80
83
|
defaults: StrMapping | None = None,
|
84
|
+
start: MaybeCallableDateTime | None = _START,
|
81
85
|
version: MaybeCallableVersionLike | None = None,
|
82
86
|
max_width: int = RICH_MAX_WIDTH,
|
83
87
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -89,7 +93,8 @@ class RichTracebackFormatter(Formatter):
|
|
89
93
|
post: Callable[[str], str] | None = None,
|
90
94
|
) -> None:
|
91
95
|
super().__init__(fmt, datefmt, style, validate, defaults=defaults)
|
92
|
-
self.
|
96
|
+
self._start = get_datetime(datetime=start)
|
97
|
+
self._version = get_version(version=version)
|
93
98
|
self._max_width = max_width
|
94
99
|
self._indent_size = indent_size
|
95
100
|
self._max_length = max_length
|
@@ -110,6 +115,7 @@ class RichTracebackFormatter(Formatter):
|
|
110
115
|
exc_value = ensure_not_none(exc_value, desc="exc_value")
|
111
116
|
error = get_rich_traceback(
|
112
117
|
exc_value,
|
118
|
+
start=self._start,
|
113
119
|
version=self._version,
|
114
120
|
max_width=self._max_width,
|
115
121
|
indent_size=self._indent_size,
|
@@ -263,6 +269,7 @@ class ExcChainTB(Generic[TBaseException]):
|
|
263
269
|
errors: list[
|
264
270
|
ExcGroupTB[TBaseException] | ExcTB[TBaseException] | TBaseException
|
265
271
|
] = field(default_factory=list)
|
272
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
266
273
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
267
274
|
max_width: int = RICH_MAX_WIDTH
|
268
275
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -292,7 +299,7 @@ class ExcChainTB(Generic[TBaseException]):
|
|
292
299
|
"""Format the traceback."""
|
293
300
|
lines: list[str] = []
|
294
301
|
if header: # pragma: no cover
|
295
|
-
lines.extend(_yield_header_lines(version=self.version))
|
302
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
296
303
|
total = len(self.errors)
|
297
304
|
for i, errors in enumerate(self.errors, start=1):
|
298
305
|
lines.append(f"Exception chain {i}/{total}:")
|
@@ -315,6 +322,7 @@ class ExcGroupTB(Generic[TBaseException]):
|
|
315
322
|
errors: list[
|
316
323
|
ExcGroupTB[TBaseException] | ExcTB[TBaseException] | TBaseException
|
317
324
|
] = field(default_factory=list)
|
325
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
318
326
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
319
327
|
max_width: int = RICH_MAX_WIDTH
|
320
328
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -333,7 +341,7 @@ class ExcGroupTB(Generic[TBaseException]):
|
|
333
341
|
"""Format the traceback."""
|
334
342
|
lines: list[str] = [] # skipif-ci
|
335
343
|
if header: # pragma: no cover
|
336
|
-
lines.extend(_yield_header_lines(version=self.version))
|
344
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
337
345
|
lines.append("Exception group:") # skipif-ci
|
338
346
|
match self.exc_group: # skipif-ci
|
339
347
|
case ExcTB() as exc_tb:
|
@@ -363,6 +371,7 @@ class ExcTB(Generic[TBaseException]):
|
|
363
371
|
|
364
372
|
frames: list[_Frame] = field(default_factory=list)
|
365
373
|
error: TBaseException
|
374
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
366
375
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
367
376
|
max_width: int = RICH_MAX_WIDTH
|
368
377
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -391,7 +400,7 @@ class ExcTB(Generic[TBaseException]):
|
|
391
400
|
total = len(self)
|
392
401
|
lines: list[str] = []
|
393
402
|
if header: # pragma: no cover
|
394
|
-
lines.extend(_yield_header_lines(version=self.version))
|
403
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
395
404
|
for i, frame in enumerate(self.frames):
|
396
405
|
is_head = i < total - 1
|
397
406
|
lines.append(
|
@@ -485,6 +494,7 @@ def get_rich_traceback(
|
|
485
494
|
error: TBaseException,
|
486
495
|
/,
|
487
496
|
*,
|
497
|
+
start: MaybeCallableDateTime | None = _START,
|
488
498
|
version: MaybeCallableVersionLike | None = None,
|
489
499
|
max_width: int = RICH_MAX_WIDTH,
|
490
500
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -506,6 +516,7 @@ def get_rich_traceback(
|
|
506
516
|
err_recast = cast("TBaseException", err)
|
507
517
|
return _get_rich_traceback_non_chain(
|
508
518
|
err_recast,
|
519
|
+
start=start,
|
509
520
|
version=version,
|
510
521
|
max_width=max_width,
|
511
522
|
indent_size=indent_size,
|
@@ -520,6 +531,7 @@ def get_rich_traceback(
|
|
520
531
|
errors=[
|
521
532
|
_get_rich_traceback_non_chain(
|
522
533
|
e,
|
534
|
+
start=start,
|
523
535
|
version=version,
|
524
536
|
max_width=max_width,
|
525
537
|
indent_size=indent_size,
|
@@ -530,6 +542,7 @@ def get_rich_traceback(
|
|
530
542
|
)
|
531
543
|
for e in errs_recast
|
532
544
|
],
|
545
|
+
start=start,
|
533
546
|
version=version,
|
534
547
|
max_width=max_width,
|
535
548
|
indent_size=indent_size,
|
@@ -544,6 +557,7 @@ def _get_rich_traceback_non_chain(
|
|
544
557
|
error: ExceptionGroup[Any] | TBaseException,
|
545
558
|
/,
|
546
559
|
*,
|
560
|
+
start: MaybeCallableDateTime | None = _START,
|
547
561
|
version: MaybeCallableVersionLike | None = None,
|
548
562
|
max_width: int = RICH_MAX_WIDTH,
|
549
563
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -567,6 +581,7 @@ def _get_rich_traceback_non_chain(
|
|
567
581
|
errors = [
|
568
582
|
_get_rich_traceback_non_chain(
|
569
583
|
e,
|
584
|
+
start=start,
|
570
585
|
version=version,
|
571
586
|
max_width=max_width,
|
572
587
|
indent_size=indent_size,
|
@@ -580,6 +595,7 @@ def _get_rich_traceback_non_chain(
|
|
580
595
|
return ExcGroupTB(
|
581
596
|
exc_group=exc_group_or_exc_tb,
|
582
597
|
errors=errors,
|
598
|
+
start=start,
|
583
599
|
version=version,
|
584
600
|
max_width=max_width,
|
585
601
|
indent_size=indent_size,
|
@@ -591,6 +607,7 @@ def _get_rich_traceback_non_chain(
|
|
591
607
|
case BaseException() as base_exc:
|
592
608
|
return _get_rich_traceback_base_one(
|
593
609
|
base_exc,
|
610
|
+
start=start,
|
594
611
|
version=version,
|
595
612
|
max_width=max_width,
|
596
613
|
indent_size=indent_size,
|
@@ -607,6 +624,7 @@ def _get_rich_traceback_base_one(
|
|
607
624
|
error: TBaseException,
|
608
625
|
/,
|
609
626
|
*,
|
627
|
+
start: MaybeCallableDateTime | None = _START,
|
610
628
|
version: MaybeCallableVersionLike | None = None,
|
611
629
|
max_width: int = RICH_MAX_WIDTH,
|
612
630
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -638,6 +656,7 @@ def _get_rich_traceback_base_one(
|
|
638
656
|
return ExcTB(
|
639
657
|
frames=frames,
|
640
658
|
error=error,
|
659
|
+
start=start,
|
641
660
|
version=version,
|
642
661
|
max_width=max_width,
|
643
662
|
indent_size=indent_size,
|
@@ -793,13 +812,25 @@ def _merge_frames(
|
|
793
812
|
|
794
813
|
|
795
814
|
def _yield_header_lines(
|
796
|
-
*,
|
815
|
+
*,
|
816
|
+
start: MaybeCallableDateTime | None = _START,
|
817
|
+
version: MaybeCallableVersionLike | None = None,
|
797
818
|
) -> Iterator[str]:
|
798
819
|
"""Yield the header lines."""
|
799
|
-
from utilities.tzlocal import get_now_local
|
820
|
+
from utilities.tzlocal import get_local_time_zone, get_now_local
|
800
821
|
from utilities.whenever import serialize_zoned_datetime
|
801
822
|
|
802
|
-
|
823
|
+
now = get_now_local()
|
824
|
+
start_use = get_datetime(datetime=start)
|
825
|
+
start_use = (
|
826
|
+
None if start_use is None else start_use.astimezone(get_local_time_zone())
|
827
|
+
)
|
828
|
+
yield f"Date/time | {serialize_zoned_datetime(now)}"
|
829
|
+
start_str = "" if start_use is None else serialize_zoned_datetime(start_use)
|
830
|
+
yield f"Started | {start_str}"
|
831
|
+
duration = None if start_use is None else (now - start_use)
|
832
|
+
duration_str = "" if duration is None else serialize_duration(duration)
|
833
|
+
yield f"Duration | {duration_str}"
|
803
834
|
yield f"User | {getuser()}"
|
804
835
|
yield f"Host | {gethostname()}"
|
805
836
|
version_use = "" if version is None else get_version(version=version)
|
utilities/whenever.py
CHANGED
@@ -4,7 +4,9 @@ import datetime as dt
|
|
4
4
|
import re
|
5
5
|
from contextlib import suppress
|
6
6
|
from dataclasses import dataclass
|
7
|
-
from
|
7
|
+
from functools import cache
|
8
|
+
from logging import LogRecord
|
9
|
+
from typing import TYPE_CHECKING, Any, override
|
8
10
|
|
9
11
|
from whenever import (
|
10
12
|
Date,
|
@@ -33,6 +35,8 @@ from utilities.re import (
|
|
33
35
|
from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
|
34
36
|
|
35
37
|
if TYPE_CHECKING:
|
38
|
+
from zoneinfo import ZoneInfo
|
39
|
+
|
36
40
|
from utilities.types import (
|
37
41
|
DateLike,
|
38
42
|
DateTimeLike,
|
@@ -561,6 +565,64 @@ class SerializeZonedDateTimeError(Exception):
|
|
561
565
|
##
|
562
566
|
|
563
567
|
|
568
|
+
class WheneverLogRecord(LogRecord):
|
569
|
+
"""Log record powered by `whenever`."""
|
570
|
+
|
571
|
+
zoned_datetime: str
|
572
|
+
|
573
|
+
@override
|
574
|
+
def __init__(
|
575
|
+
self,
|
576
|
+
name: str,
|
577
|
+
level: int,
|
578
|
+
pathname: str,
|
579
|
+
lineno: int,
|
580
|
+
msg: object,
|
581
|
+
args: Any,
|
582
|
+
exc_info: Any,
|
583
|
+
func: str | None = None,
|
584
|
+
sinfo: str | None = None,
|
585
|
+
) -> None:
|
586
|
+
super().__init__(
|
587
|
+
name, level, pathname, lineno, msg, args, exc_info, func, sinfo
|
588
|
+
)
|
589
|
+
length = self._get_length()
|
590
|
+
plain = format(self._get_now().to_plain().format_common_iso(), f"{length}s")
|
591
|
+
time_zone = self._get_time_zone_key()
|
592
|
+
self.zoned_datetime = f"{plain}[{time_zone}]"
|
593
|
+
|
594
|
+
@classmethod
|
595
|
+
@cache
|
596
|
+
def _get_time_zone(cls) -> ZoneInfo:
|
597
|
+
"""Get the local timezone."""
|
598
|
+
try:
|
599
|
+
from utilities.tzlocal import get_local_time_zone
|
600
|
+
except ModuleNotFoundError: # pragma: no cover
|
601
|
+
return UTC
|
602
|
+
return get_local_time_zone()
|
603
|
+
|
604
|
+
@classmethod
|
605
|
+
@cache
|
606
|
+
def _get_time_zone_key(cls) -> str:
|
607
|
+
"""Get the local timezone as a string."""
|
608
|
+
return cls._get_time_zone().key
|
609
|
+
|
610
|
+
@classmethod
|
611
|
+
@cache
|
612
|
+
def _get_length(cls) -> int:
|
613
|
+
"""Get maximum length of a formatted string."""
|
614
|
+
now = cls._get_now().replace(nanosecond=1000).to_plain()
|
615
|
+
return len(now.format_common_iso())
|
616
|
+
|
617
|
+
@classmethod
|
618
|
+
def _get_now(cls) -> ZonedDateTime:
|
619
|
+
"""Get the current zoned datetime."""
|
620
|
+
return ZonedDateTime.now(cls._get_time_zone().key)
|
621
|
+
|
622
|
+
|
623
|
+
##
|
624
|
+
|
625
|
+
|
564
626
|
def _to_datetime_delta(timedelta: dt.timedelta, /) -> DateTimeDelta:
|
565
627
|
"""Serialize a timedelta."""
|
566
628
|
total_microseconds = datetime_duration_to_microseconds(timedelta)
|
@@ -610,6 +672,7 @@ __all__ = [
|
|
610
672
|
"SerializePlainDateTimeError",
|
611
673
|
"SerializeTimeDeltaError",
|
612
674
|
"SerializeZonedDateTimeError",
|
675
|
+
"WheneverLogRecord",
|
613
676
|
"check_valid_zoned_datetime",
|
614
677
|
"ensure_date",
|
615
678
|
"ensure_datetime",
|
File without changes
|
File without changes
|