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.
- {dycw_utilities-0.131.16.dist-info → dycw_utilities-0.131.18.dist-info}/METADATA +1 -1
- {dycw_utilities-0.131.16.dist-info → dycw_utilities-0.131.18.dist-info}/RECORD +18 -19
- utilities/__init__.py +1 -1
- utilities/asyncio.py +47 -60
- utilities/datetime.py +2 -279
- utilities/iterables.py +1 -30
- utilities/operator.py +21 -15
- utilities/orjson.py +67 -84
- utilities/parse.py +24 -53
- utilities/pottery.py +16 -19
- utilities/psutil.py +8 -7
- utilities/redis.py +108 -116
- utilities/slack_sdk.py +17 -18
- utilities/sqlalchemy.py +36 -30
- utilities/sqlalchemy_polars.py +12 -27
- utilities/whenever.py +2 -216
- utilities/tenacity.py +0 -145
- {dycw_utilities-0.131.16.dist-info → dycw_utilities-0.131.18.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.16.dist-info → dycw_utilities-0.131.18.dist-info}/licenses/LICENSE +0 -0
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
|
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
|
-
|
66
|
+
date_delta = "dd"
|
67
|
+
date_time_delta = "D"
|
74
68
|
enum = "e"
|
75
|
-
exception_class = "
|
76
|
-
exception_instance = "
|
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
|
-
|
84
|
-
neg_inf = "neg_inf"
|
76
|
+
plain_date_time = "pd"
|
85
77
|
set_ = "s"
|
86
|
-
|
87
|
-
|
78
|
+
time = "ti"
|
79
|
+
time_delta = "td"
|
88
80
|
tuple_ = "tu"
|
89
81
|
unserializable = "un"
|
90
82
|
uuid = "uu"
|
91
83
|
version = "v"
|
92
|
-
|
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
|
164
|
-
return f"[{_Prefixes.
|
165
|
-
case
|
166
|
-
return f"[{_Prefixes.
|
167
|
-
case
|
168
|
-
return f"[{_Prefixes.
|
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.
|
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
|
-
|
346
|
+
_TIME_DELTA_PATTERN,
|
347
|
+
_UUID_PATTERN,
|
370
348
|
_VERSION_PATTERN,
|
371
|
-
|
372
|
-
|
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.
|
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
|
-
) =
|
398
|
-
|
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
|
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
|
437
|
-
if match :=
|
438
|
-
return
|
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 :=
|
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
|
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
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
|
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
|
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:
|
29
|
-
timeout_release:
|
30
|
-
sleep:
|
31
|
-
throttle:
|
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=
|
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
|
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:
|
70
|
-
sleep:
|
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
|
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
|
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:
|
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.
|
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
|
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:
|
29
|
-
backoff:
|
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.
|
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:
|
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)
|