dycw-utilities 0.149.9__py3-none-any.whl → 0.149.11__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.149.9
3
+ Version: 0.149.11
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
- utilities/__init__.py,sha256=7UA6ND9i7Ndyg5xutxO01u8nzYBz_PGZ-m8ljketKwg,60
1
+ utilities/__init__.py,sha256=ODa44fHOmk12CX4kZdMwXIvOfgua6psr4jhzHKVTZf0,61
2
2
  utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
3
- utilities/asyncio.py,sha256=z0w3fb-U5Ml5YXVaFFPClizXaQmjDO6YgZg-V9QL0VQ,16021
3
+ utilities/asyncio.py,sha256=2m2a2C-Qgc6OHTTHL332-t66A7xDITt_SORT7a1DJWo,16792
4
4
  utilities/atomicwrites.py,sha256=xcOWenTBRS0oat3kg7Sqe51AohNThMQ2ixPL7QCG8hw,5795
5
5
  utilities/atools.py,sha256=9im2g8OCf-Iynqa8bAv8N0Ycj9QvrJmGO7yLCZEdgII,986
6
6
  utilities/cachetools.py,sha256=v1-9sXHLdOLiwmkq6NB0OUbxeKBuVVN6wmAWefWoaHI,2744
@@ -50,7 +50,7 @@ utilities/platform.py,sha256=Ue9LSxYvg9yUXGKuz5aZoy_qkUEXde-v6B09exgSctU,2813
50
50
  utilities/polars.py,sha256=BgiDryAVOapi41ddfJqN0wYh_sDj8BNEYtPB36LaHdo,71824
51
51
  utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
52
52
  utilities/postgres.py,sha256=C3TxpQymM7S5ZivxIN_AFTM5g5TtENptzx8-XYf7pow,12182
53
- utilities/pottery.py,sha256=cdFBjU3Zn-nhlhk4p8IC-ybwFTzk0F2TE1xBV7z8V3c,6296
53
+ utilities/pottery.py,sha256=u0uvyGgYyujxftEMlsv6ppYTKQoVVjHt5jnVxxYz9s4,6596
54
54
  utilities/pqdm.py,sha256=BTsYPtbKQWwX-iXF4qCkfPG7DPxIB54J989n83bXrIo,3092
55
55
  utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
56
56
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -63,7 +63,7 @@ utilities/redis.py,sha256=MNxDTbTQTkUOtIikJY9UbfozTC3zuJpAUBsw02LLXTA,28377
63
63
  utilities/reprlib.py,sha256=ssYTcBW-TeRh3fhCJv57sopTZHF5FrPyyUg9yp5XBlo,3953
64
64
  utilities/scipy.py,sha256=wZJM7fEgBAkLSYYvSmsg5ac-QuwAI0BGqHVetw1_Hb0,947
65
65
  utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
66
- utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
66
+ utilities/shelve.py,sha256=4OzjQI6kGuUbJciqf535rwnao-_IBv66gsT6tRGiUt0,759
67
67
  utilities/slack_sdk.py,sha256=ppFBvKgfg5IRWiIoKPtpTyzBtBF4XmwEvU3I5wLJikM,2140
68
68
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
69
69
  utilities/sqlalchemy.py,sha256=q2aYUDAC3SE88Lt6XaKa3CLzT_ePaWvQu_OuRk19x9g,35520
@@ -89,8 +89,8 @@ utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
89
89
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
90
90
  utilities/pytest_plugins/pytest_randomly.py,sha256=NXzCcGKbpgYouz5yehKb4jmxmi2SexKKpgF4M65bi10,414
91
91
  utilities/pytest_plugins/pytest_regressions.py,sha256=Iwhfv_OJH7UCPZCfoh7ugZ2Xjqjil-BBBsOb8sDwiGI,1471
92
- dycw_utilities-0.149.9.dist-info/METADATA,sha256=a_Tgzi2aFO5cYWwQzBVoiQVZQ8cqQgHK1xouAaeSvnU,1697
93
- dycw_utilities-0.149.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- dycw_utilities-0.149.9.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
- dycw_utilities-0.149.9.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
- dycw_utilities-0.149.9.dist-info/RECORD,,
92
+ dycw_utilities-0.149.11.dist-info/METADATA,sha256=wXNAZioNfRMvkYH6pANvwG5RbGy1D-9z7hSwmANMABA,1698
93
+ dycw_utilities-0.149.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ dycw_utilities-0.149.11.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
+ dycw_utilities-0.149.11.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
+ dycw_utilities-0.149.11.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.149.9"
3
+ __version__ = "0.149.11"
utilities/asyncio.py CHANGED
@@ -22,6 +22,7 @@ from contextlib import (
22
22
  )
23
23
  from dataclasses import dataclass
24
24
  from io import StringIO
25
+ from pathlib import Path
25
26
  from subprocess import PIPE
26
27
  from sys import stderr, stdout
27
28
  from typing import (
@@ -40,6 +41,7 @@ from utilities.functions import ensure_int, ensure_not_none, to_bool
40
41
  from utilities.logging import get_logger
41
42
  from utilities.random import SYSTEM_RANDOM
42
43
  from utilities.sentinel import Sentinel, sentinel
44
+ from utilities.shelve import yield_shelf
43
45
  from utilities.warnings import suppress_warnings
44
46
  from utilities.whenever import get_now, round_date_or_date_time, to_nanoseconds
45
47
 
@@ -58,10 +60,12 @@ if TYPE_CHECKING:
58
60
  )
59
61
  from contextvars import Context
60
62
  from random import Random
63
+ from shelve import Shelf
61
64
  from types import TracebackType
62
65
 
63
66
  from whenever import ZonedDateTime
64
67
 
68
+ from utilities.shelve import _Flag
65
69
  from utilities.types import (
66
70
  Coro,
67
71
  Delta,
@@ -69,6 +73,7 @@ if TYPE_CHECKING:
69
73
  LoggerOrName,
70
74
  MaybeCallableBool,
71
75
  MaybeType,
76
+ PathLike,
72
77
  SupportsKeysAndGetItem,
73
78
  )
74
79
 
@@ -547,6 +552,35 @@ async def timeout_td(
547
552
  raise error from None
548
553
 
549
554
 
555
+ ##
556
+
557
+
558
+ _LOCKS: AsyncDict[Path, Lock] = AsyncDict()
559
+
560
+
561
+ @asynccontextmanager
562
+ async def yield_locked_shelf(
563
+ path: PathLike,
564
+ /,
565
+ *,
566
+ flag: _Flag = "c",
567
+ protocol: int | None = None,
568
+ writeback: bool = False,
569
+ ) -> AsyncIterator[Shelf[Any]]:
570
+ """Yield a shelf, behind a lock."""
571
+ path = Path(path)
572
+ try:
573
+ lock = _LOCKS[path]
574
+ except KeyError:
575
+ lock = Lock()
576
+ await _LOCKS.set(path, lock)
577
+ async with lock:
578
+ with yield_shelf(
579
+ path, flag=flag, protocol=protocol, writeback=writeback
580
+ ) as shelf:
581
+ yield shelf
582
+
583
+
550
584
  __all__ = [
551
585
  "AsyncDict",
552
586
  "EnhancedTaskGroup",
@@ -563,4 +597,5 @@ __all__ = [
563
597
  "sleep_until",
564
598
  "stream_command",
565
599
  "timeout_td",
600
+ "yield_locked_shelf",
566
601
  ]
utilities/pottery.py CHANGED
@@ -12,12 +12,14 @@ from redis.asyncio import Redis
12
12
 
13
13
  from utilities.asyncio import loop_until_succeed, sleep_td, timeout_td
14
14
  from utilities.contextlib import enhanced_async_context_manager
15
+ from utilities.contextvars import yield_set_context
15
16
  from utilities.iterables import always_iterable
16
17
  from utilities.logging import get_logger
17
18
  from utilities.whenever import MILLISECOND, SECOND, to_seconds
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from collections.abc import AsyncIterator, Callable, Iterable
22
+ from contextvars import ContextVar
21
23
 
22
24
  from whenever import Delta
23
25
 
@@ -57,6 +59,7 @@ async def try_yield_coroutine_looper(
57
59
  throttle: Delta | None = None,
58
60
  logger: LoggerOrName | None = None,
59
61
  sleep_error: Delta | None = None,
62
+ context: ContextVar[bool] | None = None,
60
63
  ) -> AsyncIterator[CoroutineLooper | None]:
61
64
  """Try acquire access to a coroutine looper."""
62
65
  try: # skipif-ci-and-not-linux
@@ -70,7 +73,12 @@ async def try_yield_coroutine_looper(
70
73
  sleep=sleep_acquire,
71
74
  throttle=throttle,
72
75
  ) as lock:
73
- yield CoroutineLooper(lock=lock, logger=logger, sleep=sleep_error)
76
+ looper = CoroutineLooper(lock=lock, logger=logger, sleep=sleep_error)
77
+ if context is None:
78
+ yield looper
79
+ else:
80
+ with yield_set_context(context):
81
+ yield looper
74
82
  except _YieldAccessUnableToAcquireLockError as error: # skipif-ci-and-not-linux
75
83
  if logger is not None:
76
84
  get_logger(logger=logger).info("%s", error)
utilities/shelve.py CHANGED
@@ -12,12 +12,15 @@ if TYPE_CHECKING:
12
12
  from utilities.types import PathLike
13
13
 
14
14
 
15
+ type _Flag = Literal["r", "w", "c", "n"]
16
+
17
+
15
18
  @contextmanager
16
19
  def yield_shelf(
17
20
  path: PathLike,
18
21
  /,
19
22
  *,
20
- flag: Literal["r", "w", "c", "n"] = "c",
23
+ flag: _Flag = "c",
21
24
  protocol: int | None = None,
22
25
  writeback: bool = False,
23
26
  ) -> Iterator[Shelf[Any]]: