dycw-utilities 0.131.16__py3-none-any.whl → 0.131.18__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.
utilities/orjson.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime as dt
4
3
  import re
5
4
  from collections.abc import Callable, Iterable, Mapping, Sequence
6
5
  from contextlib import suppress
@@ -23,7 +22,15 @@ from orjson import (
23
22
  dumps,
24
23
  loads,
25
24
  )
26
- from whenever import ZonedDateTime
25
+ from whenever import (
26
+ Date,
27
+ DateDelta,
28
+ DateTimeDelta,
29
+ PlainDateTime,
30
+ Time,
31
+ TimeDelta,
32
+ ZonedDateTime,
33
+ )
27
34
 
28
35
  from utilities.concurrent import concurrent_map
29
36
  from utilities.dataclasses import dataclass_to_dict
@@ -39,27 +46,13 @@ from utilities.logging import get_logging_level_number
39
46
  from utilities.math import MAX_INT64, MIN_INT64
40
47
  from utilities.types import Dataclass, LogLevel, MaybeIterable, PathLike, StrMapping
41
48
  from utilities.tzlocal import LOCAL_TIME_ZONE
42
- from utilities.uuid import UUID_PATTERN
43
49
  from utilities.version import Version, parse_version
44
- from utilities.whenever import (
45
- parse_date,
46
- parse_plain_datetime,
47
- parse_time,
48
- parse_timedelta,
49
- parse_zoned_datetime,
50
- serialize_date,
51
- serialize_datetime,
52
- serialize_time,
53
- serialize_timedelta,
54
- )
55
50
  from utilities.whenever2 import from_timestamp
56
51
 
57
52
  if TYPE_CHECKING:
58
53
  from collections.abc import Set as AbstractSet
59
54
  from logging import _FormatStyle
60
55
 
61
- from whenever import Date
62
-
63
56
  from utilities.types import Parallelism
64
57
 
65
58
 
@@ -70,26 +63,25 @@ if TYPE_CHECKING:
70
63
  class _Prefixes(Enum):
71
64
  dataclass = "dc"
72
65
  date = "d"
73
- datetime = "dt"
66
+ date_delta = "dd"
67
+ date_time_delta = "D"
74
68
  enum = "e"
75
- exception_class = "exc"
76
- exception_instance = "exi"
69
+ exception_class = "Ex"
70
+ exception_instance = "ex"
77
71
  float_ = "fl"
78
72
  frozenset_ = "fr"
79
73
  list_ = "l"
80
- nan = "nan"
81
74
  none = "none"
82
75
  path = "p"
83
- pos_inf = "pos_inf"
84
- neg_inf = "neg_inf"
76
+ plain_date_time = "pd"
85
77
  set_ = "s"
86
- timedelta = "td"
87
- time = "tm"
78
+ time = "ti"
79
+ time_delta = "td"
88
80
  tuple_ = "tu"
89
81
  unserializable = "un"
90
82
  uuid = "uu"
91
83
  version = "v"
92
- zoned_datetime = "zd"
84
+ zoned_date_time = "zd"
93
85
 
94
86
 
95
87
  type _DataclassHook = Callable[[type[Dataclass], StrMapping], StrMapping]
@@ -160,14 +152,12 @@ def _pre_process(
160
152
  # singletons
161
153
  case None:
162
154
  return f"[{_Prefixes.none.value}]"
163
- case dt.datetime() as datetime:
164
- return f"[{_Prefixes.datetime.value}]{serialize_datetime(datetime)}"
165
- case dt.date() as date: # after datetime
166
- return f"[{_Prefixes.date.value}]{serialize_date(date)}"
167
- case dt.time() as time:
168
- return f"[{_Prefixes.time.value}]{serialize_time(time)}"
169
- case dt.timedelta() as timedelta:
170
- return f"[{_Prefixes.timedelta.value}]{serialize_timedelta(timedelta)}"
155
+ case Date() as date:
156
+ return f"[{_Prefixes.date.value}]{date}"
157
+ case DateDelta() as date:
158
+ return f"[{_Prefixes.date_delta.value}]{date}"
159
+ case DateTimeDelta() as date:
160
+ return f"[{_Prefixes.date_time_delta.value}]{date}"
171
161
  case Exception() as error_:
172
162
  return {
173
163
  f"[{_Prefixes.exception_instance.value}|{type(error_).__qualname__}]": pre(
@@ -182,18 +172,24 @@ def _pre_process(
182
172
  if MIN_INT64 <= int_ <= MAX_INT64:
183
173
  return int_
184
174
  raise _SerializeIntegerError(obj=int_)
185
- case UUID() as uuid:
186
- return f"[{_Prefixes.uuid.value}]{uuid}"
187
175
  case Path() as path:
188
176
  return f"[{_Prefixes.path.value}]{path!s}"
177
+ case PlainDateTime() as datetime:
178
+ return f"[{_Prefixes.plain_date_time.value}]{datetime}"
189
179
  case str() as str_:
190
180
  return str_
181
+ case Time() as time:
182
+ return f"[{_Prefixes.time.value}]{time}"
183
+ case TimeDelta() as time_delta:
184
+ return f"[{_Prefixes.time_delta.value}]{time_delta}"
191
185
  case type() as error_cls if issubclass(error_cls, Exception):
192
186
  return f"[{_Prefixes.exception_class.value}|{error_cls.__qualname__}]"
187
+ case UUID() as uuid:
188
+ return f"[{_Prefixes.uuid.value}]{uuid}"
193
189
  case Version() as version:
194
190
  return f"[{_Prefixes.version.value}]{version!s}"
195
191
  case ZonedDateTime() as datetime:
196
- return f"[{_Prefixes.zoned_datetime.value}]{datetime}"
192
+ return f"[{_Prefixes.zoned_date_time.value}]{datetime}"
197
193
  # contains
198
194
  case Dataclass() as dataclass:
199
195
  asdict = dataclass_to_dict(
@@ -338,51 +334,36 @@ def deserialize(
338
334
  )
339
335
 
340
336
 
341
- _NONE_PATTERN = re.compile(r"^\[" + _Prefixes.none.value + r"\]$")
342
- _LOCAL_DATETIME_PATTERN = re.compile(
343
- r"^\["
344
- + _Prefixes.datetime.value
345
- + r"\](\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?)$"
346
- )
347
- _UUID_PATTERN = re.compile(r"^\[" + _Prefixes.uuid.value + r"\](" + UUID_PATTERN + ")$")
348
- _ZONED_DATETIME_PATTERN = re.compile(
349
- r"^\["
350
- + _Prefixes.datetime.value
351
- + r"\](\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?[\+\-]\d{2}:\d{2}(?::\d{2})?\[(?!(?:dt\.)).+?\])$"
352
- )
353
- _ZONED_DATETIME_PATTERN2 = re.compile(
354
- r"^\["
355
- + _Prefixes.zoned_datetime.value
356
- + r"\](\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?[\+\-]\d{2}:\d{2}(?::\d{2})?\[(?!(?:dt\.)).+?\])$"
357
- )
358
-
359
-
360
- def _make_unit_pattern(prefix: _Prefixes, /) -> Pattern[str]:
361
- return re.compile(r"^\[" + prefix.value + r"\](.+)$")
362
-
363
-
364
337
  (
365
338
  _DATE_PATTERN,
339
+ _DATE_DELTA_PATTERN,
340
+ _DATE_TIME_DELTA_PATTERN,
366
341
  _FLOAT_PATTERN,
342
+ _NONE_PATTERN,
367
343
  _PATH_PATTERN,
344
+ _PLAIN_DATE_TIME_PATTERN,
368
345
  _TIME_PATTERN,
369
- _TIMEDELTA_PATTERN,
346
+ _TIME_DELTA_PATTERN,
347
+ _UUID_PATTERN,
370
348
  _VERSION_PATTERN,
371
- ) = map(
372
- _make_unit_pattern,
373
- [
349
+ _ZONED_DATE_TIME_PATTERN,
350
+ ) = [
351
+ re.compile(r"^\[" + p.value + r"\](" + ".*" + ")$")
352
+ for p in [
374
353
  _Prefixes.date,
354
+ _Prefixes.date_delta,
355
+ _Prefixes.date_time_delta,
375
356
  _Prefixes.float_,
357
+ _Prefixes.none,
376
358
  _Prefixes.path,
359
+ _Prefixes.plain_date_time,
377
360
  _Prefixes.time,
378
- _Prefixes.timedelta,
361
+ _Prefixes.time_delta,
362
+ _Prefixes.uuid,
379
363
  _Prefixes.version,
380
- ],
381
- )
382
-
383
-
384
- def _make_container_pattern(prefix: _Prefixes, /) -> Pattern[str]:
385
- return re.compile(r"^\[" + prefix.value + r"(?:\|(.+))?\]$")
364
+ _Prefixes.zoned_date_time,
365
+ ]
366
+ ]
386
367
 
387
368
 
388
369
  (
@@ -394,9 +375,9 @@ def _make_container_pattern(prefix: _Prefixes, /) -> Pattern[str]:
394
375
  _LIST_PATTERN,
395
376
  _SET_PATTERN,
396
377
  _TUPLE_PATTERN,
397
- ) = map(
398
- _make_container_pattern,
399
- [
378
+ ) = [
379
+ re.compile(r"^\[" + p.value + r"(?:\|(.+))?\]$")
380
+ for p in [
400
381
  _Prefixes.dataclass,
401
382
  _Prefixes.enum,
402
383
  _Prefixes.exception_class,
@@ -405,8 +386,8 @@ def _make_container_pattern(prefix: _Prefixes, /) -> Pattern[str]:
405
386
  _Prefixes.list_,
406
387
  _Prefixes.set_,
407
388
  _Prefixes.tuple_,
408
- ],
409
- )
389
+ ]
390
+ ]
410
391
 
411
392
 
412
393
  def _object_hook(
@@ -425,24 +406,26 @@ def _object_hook(
425
406
  if match := _NONE_PATTERN.search(text):
426
407
  return None
427
408
  if match := _DATE_PATTERN.search(text):
428
- return parse_date(match.group(1))
409
+ return Date.parse_common_iso(match.group(1))
410
+ if match := _DATE_DELTA_PATTERN.search(text):
411
+ return DateDelta.parse_common_iso(match.group(1))
412
+ if match := _DATE_TIME_DELTA_PATTERN.search(text):
413
+ return DateTimeDelta.parse_common_iso(match.group(1))
429
414
  if match := _FLOAT_PATTERN.search(text):
430
415
  return float(match.group(1))
431
- if match := _LOCAL_DATETIME_PATTERN.search(text):
432
- return parse_plain_datetime(match.group(1))
433
416
  if match := _PATH_PATTERN.search(text):
434
417
  return Path(match.group(1))
418
+ if match := _PLAIN_DATE_TIME_PATTERN.search(text):
419
+ return PlainDateTime.parse_common_iso(match.group(1))
435
420
  if match := _TIME_PATTERN.search(text):
436
- return parse_time(match.group(1))
437
- if match := _TIMEDELTA_PATTERN.search(text):
438
- return parse_timedelta(match.group(1))
421
+ return Time.parse_common_iso(match.group(1))
422
+ if match := _TIME_DELTA_PATTERN.search(text):
423
+ return TimeDelta.parse_common_iso(match.group(1))
439
424
  if match := _UUID_PATTERN.search(text):
440
425
  return UUID(match.group(1))
441
426
  if match := _VERSION_PATTERN.search(text):
442
427
  return parse_version(match.group(1))
443
- if match := _ZONED_DATETIME_PATTERN.search(text):
444
- return parse_zoned_datetime(match.group(1))
445
- if match := _ZONED_DATETIME_PATTERN2.search(text):
428
+ if match := _ZONED_DATE_TIME_PATTERN.search(text):
446
429
  return ZonedDateTime.parse_common_iso(match.group(1))
447
430
  if (
448
431
  exc_class := _object_hook_exception_class(
utilities/parse.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime as dt
4
3
  from contextlib import suppress
5
4
  from dataclasses import dataclass
6
5
  from enum import Enum
@@ -9,6 +8,16 @@ from re import DOTALL
9
8
  from types import NoneType
10
9
  from typing import TYPE_CHECKING, Any, override
11
10
 
11
+ from whenever import (
12
+ Date,
13
+ DateDelta,
14
+ DateTimeDelta,
15
+ PlainDateTime,
16
+ Time,
17
+ TimeDelta,
18
+ ZonedDateTime,
19
+ )
20
+
12
21
  from utilities.enum import ParseEnumError, parse_enum
13
22
  from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
14
23
  from utilities.math import ParseNumberError, parse_number
@@ -26,7 +35,7 @@ from utilities.text import (
26
35
  split_key_value_pairs,
27
36
  split_str,
28
37
  )
29
- from utilities.types import Duration, Number, ParseObjectExtra, SerializeObjectExtra
38
+ from utilities.types import Number, ParseObjectExtra, SerializeObjectExtra
30
39
  from utilities.typing import (
31
40
  get_args,
32
41
  is_dict_type,
@@ -182,6 +191,14 @@ def _parse_object_type(
182
191
  return parse_enum(text, cls, case_sensitive=case_sensitive)
183
192
  except ParseEnumError:
184
193
  raise _ParseObjectParseError(type_=cls, text=text) from None
194
+ if issubclass(
195
+ cls,
196
+ (Date, DateDelta, DateTimeDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime),
197
+ ):
198
+ try:
199
+ return cls.parse_common_iso(text)
200
+ except ValueError:
201
+ raise _ParseObjectParseError(type_=cls, text=text) from None
185
202
  if issubclass(cls, Path):
186
203
  return Path(text).expanduser()
187
204
  if issubclass(cls, Sentinel):
@@ -194,34 +211,6 @@ def _parse_object_type(
194
211
  return parse_version(text)
195
212
  except ParseVersionError:
196
213
  raise _ParseObjectParseError(type_=cls, text=text) from None
197
- if is_subclass_gen(cls, dt.date):
198
- from utilities.whenever import ParseDateError, parse_date
199
-
200
- try:
201
- return parse_date(text)
202
- except ParseDateError:
203
- raise _ParseObjectParseError(type_=cls, text=text) from None
204
- if is_subclass_gen(cls, dt.datetime):
205
- from utilities.whenever import ParseDateTimeError, parse_datetime
206
-
207
- try:
208
- return parse_datetime(text)
209
- except ParseDateTimeError:
210
- raise _ParseObjectParseError(type_=cls, text=text) from None
211
- if issubclass(cls, dt.time):
212
- from utilities.whenever import ParseTimeError, parse_time
213
-
214
- try:
215
- return parse_time(text)
216
- except ParseTimeError:
217
- raise _ParseObjectParseError(type_=cls, text=text) from None
218
- if issubclass(cls, dt.timedelta):
219
- from utilities.whenever import ParseTimedeltaError, parse_timedelta
220
-
221
- try:
222
- return parse_timedelta(text)
223
- except ParseTimedeltaError:
224
- raise _ParseObjectParseError(type_=cls, text=text) from None
225
214
  raise _ParseObjectParseError(type_=cls, text=text)
226
215
 
227
216
 
@@ -374,13 +363,6 @@ def _parse_object_union_type(type_: Any, text: str, /) -> Any:
374
363
  return parse_number(text)
375
364
  except ParseNumberError:
376
365
  raise _ParseObjectParseError(type_=type_, text=text) from None
377
- if type_ is Duration:
378
- from utilities.whenever import ParseDurationError, parse_duration
379
-
380
- try:
381
- return parse_duration(text)
382
- except ParseDurationError:
383
- raise _ParseObjectParseError(type_=type_, text=text) from None
384
366
  raise _ParseObjectParseError(type_=type_, text=text) from None
385
367
 
386
368
 
@@ -464,22 +446,11 @@ def serialize_object(
464
446
  obj, bool | int | float | str | Path | Sentinel | Version
465
447
  ):
466
448
  return str(obj)
467
- if is_instance_gen(obj, dt.date):
468
- from utilities.whenever import serialize_date
469
-
470
- return serialize_date(obj)
471
- if is_instance_gen(obj, dt.datetime):
472
- from utilities.whenever import serialize_datetime
473
-
474
- return serialize_datetime(obj)
475
- if isinstance(obj, dt.time):
476
- from utilities.whenever import serialize_time
477
-
478
- return serialize_time(obj)
479
- if isinstance(obj, dt.timedelta):
480
- from utilities.whenever import serialize_timedelta
481
-
482
- return serialize_timedelta(obj)
449
+ if isinstance(
450
+ obj,
451
+ (Date, DateDelta, DateTimeDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime),
452
+ ):
453
+ return obj.format_common_iso()
483
454
  if isinstance(obj, Enum):
484
455
  return obj.name
485
456
  if isinstance(obj, dict):
utilities/pottery.py CHANGED
@@ -8,14 +8,16 @@ from pottery import AIORedlock
8
8
  from pottery.exceptions import ReleaseUnlockedLock
9
9
  from redis.asyncio import Redis
10
10
 
11
- from utilities.asyncio import sleep_dur, timeout_dur
12
- from utilities.datetime import MILLISECOND, SECOND, datetime_duration_to_float
11
+ from utilities.asyncio import sleep_td, timeout_td
13
12
  from utilities.iterables import always_iterable
13
+ from utilities.whenever2 import MILLISECOND, SECOND
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from collections.abc import AsyncIterator, Iterable
17
17
 
18
- from utilities.types import Duration, MaybeIterable
18
+ from whenever import TimeDelta
19
+
20
+ from utilities.types import MaybeIterable
19
21
 
20
22
 
21
23
  @asynccontextmanager
@@ -25,10 +27,10 @@ async def yield_access(
25
27
  /,
26
28
  *,
27
29
  num: int = 1,
28
- timeout_acquire: Duration | None = None,
29
- timeout_release: Duration = 10 * SECOND,
30
- sleep: Duration = MILLISECOND,
31
- throttle: Duration | None = None,
30
+ timeout_acquire: TimeDelta | None = None,
31
+ timeout_release: TimeDelta = 10 * SECOND,
32
+ sleep: TimeDelta = MILLISECOND,
33
+ throttle: TimeDelta | None = None,
32
34
  ) -> AsyncIterator[None]:
33
35
  """Acquire access to a locked resource, amongst 1 of multiple connections."""
34
36
  if num <= 0:
@@ -36,14 +38,11 @@ async def yield_access(
36
38
  masters = ( # skipif-ci-and-not-linux
37
39
  {redis} if isinstance(redis, Redis) else set(always_iterable(redis))
38
40
  )
39
- auto_release_time = datetime_duration_to_float( # skipif-ci-and-not-linux
40
- timeout_release
41
- )
42
41
  locks = [ # skipif-ci-and-not-linux
43
42
  AIORedlock(
44
43
  key=f"{key}_{i}_of_{num}",
45
44
  masters=masters,
46
- auto_release_time=auto_release_time,
45
+ auto_release_time=timeout_release.in_seconds(),
47
46
  )
48
47
  for i in range(1, num + 1)
49
48
  ]
@@ -54,7 +53,7 @@ async def yield_access(
54
53
  )
55
54
  yield
56
55
  finally: # skipif-ci-and-not-linux
57
- await sleep_dur(duration=throttle)
56
+ await sleep_td(throttle)
58
57
  if lock is not None:
59
58
  with suppress(ReleaseUnlockedLock):
60
59
  await lock.release()
@@ -66,20 +65,18 @@ async def _get_first_available_lock(
66
65
  /,
67
66
  *,
68
67
  num: int = 1,
69
- timeout: Duration | None = None,
70
- sleep: Duration | None = None,
68
+ timeout: TimeDelta | None = None,
69
+ sleep: TimeDelta | None = None,
71
70
  ) -> AIORedlock:
72
71
  locks = list(locks) # skipif-ci-and-not-linux
73
72
  error = _YieldAccessUnableToAcquireLockError( # skipif-ci-and-not-linux
74
73
  key=key, num=num, timeout=timeout
75
74
  )
76
- async with timeout_dur( # skipif-ci-and-not-linux
77
- duration=timeout, error=error
78
- ):
75
+ async with timeout_td(timeout, error=error): # skipif-ci-and-not-linux
79
76
  while True:
80
77
  if (result := await _get_first_available_lock_if_any(locks)) is not None:
81
78
  return result
82
- await sleep_dur(duration=sleep)
79
+ await sleep_td(sleep)
83
80
 
84
81
 
85
82
  async def _get_first_available_lock_if_any(
@@ -106,7 +103,7 @@ class _YieldAccessNumLocksError(YieldAccessError):
106
103
 
107
104
  @dataclass(kw_only=True, slots=True)
108
105
  class _YieldAccessUnableToAcquireLockError(YieldAccessError):
109
- timeout: Duration | None
106
+ timeout: TimeDelta | None
110
107
 
111
108
  @override
112
109
  def __str__(self) -> str:
utilities/psutil.py CHANGED
@@ -11,13 +11,14 @@ from psutil import swap_memory, virtual_memory
11
11
 
12
12
  from utilities.asyncio import Looper
13
13
  from utilities.contextlib import suppress_super_object_attribute_error
14
- from utilities.datetime import SECOND, get_now
14
+ from utilities.whenever2 import SECOND, get_now
15
15
 
16
16
  if TYPE_CHECKING:
17
- import datetime as dt
18
17
  from logging import Logger
19
18
 
20
- from utilities.types import Duration, PathLike
19
+ from whenever import TimeDelta, ZonedDateTime
20
+
21
+ from utilities.types import PathLike
21
22
 
22
23
 
23
24
  @dataclass(kw_only=True)
@@ -25,8 +26,8 @@ class MemoryMonitorService(Looper[None]):
25
26
  """Service to monitor memory usage."""
26
27
 
27
28
  # base
28
- freq: Duration = field(default=10 * SECOND, repr=False)
29
- backoff: Duration = field(default=10 * SECOND, repr=False)
29
+ freq: TimeDelta = field(default=10 * SECOND, repr=False)
30
+ backoff: TimeDelta = field(default=10 * SECOND, repr=False)
30
31
  # self
31
32
  console: str | None = field(default=None, repr=False)
32
33
  path: PathLike = "memory.txt"
@@ -46,7 +47,7 @@ class MemoryMonitorService(Looper[None]):
46
47
  await super().core()
47
48
  memory = MemoryUsage.new()
48
49
  mapping = {
49
- "datetime": memory.datetime.strftime("%Y-%m-%d %H:%M:%S"),
50
+ "datetime": memory.datetime.format_common_iso(),
50
51
  "virtual used (mb)": memory.virtual_used_mb,
51
52
  "virtual total (mb)": memory.virtual_total_mb,
52
53
  "virtual (%)": memory.virtual_pct,
@@ -68,7 +69,7 @@ class MemoryMonitorService(Looper[None]):
68
69
  class MemoryUsage:
69
70
  """A memory usage."""
70
71
 
71
- datetime: dt.datetime = field(default_factory=get_now)
72
+ datetime: ZonedDateTime = field(default_factory=get_now)
72
73
  virtual_used: int = field(repr=False)
73
74
  virtual_used_mb: int = field(init=False)
74
75
  virtual_total: int = field(repr=False)