dycw-utilities 0.132.4__py3-none-any.whl → 0.133.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.132.4
3
+ Version: 0.133.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,7 @@
1
- utilities/__init__.py,sha256=XNt4jtceKkZ5SzTlJ0nWpgaWnxz3PNjWlzmzu1iXOtI,60
1
+ utilities/__init__.py,sha256=YfKvUqWoneovoyLwbO-FdwUu0a33vS1nujwBMcawq9Y,60
2
2
  utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
3
3
  utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
4
+ utilities/arq.py,sha256=YkwvWoL930hgeU9VP8iuP3RhMf0t8sm7O8qsD9TiyWo,4688
4
5
  utilities/asyncio.py,sha256=USWMMrHqPVRr20vlIn_n5JLimyqa-5xLhuqDYWJed8A,37586
5
6
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
6
7
  utilities/atools.py,sha256=-bFGIrwYMFR7xl39j02DZMsO_u5x5_Ph7bRlBUFVYyw,1048
@@ -78,7 +79,7 @@ utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
78
79
  utilities/timer.py,sha256=oYqRQ-G-DMOOHB6a4yP5-PJDVimLnbNkMnkOj_jUmFg,2474
79
80
  utilities/traceback.py,sha256=i-790AQbTrDA8MiYyOcYPFpm48I558VR_kL_7x4ypfY,8503
80
81
  utilities/typed_settings.py,sha256=DqJsJjSit9wd_OA3KyMDpx2zatKIi5QhuARI9TPl3rk,3701
81
- utilities/types.py,sha256=ZkTndROqNbpgUa_MX4pYlkfmU9E8prMqr4UvASruhsE,19013
82
+ utilities/types.py,sha256=ZvD16TobtB47IgMo2CK_CCdJsvhrTqAZgqgqbCME2T0,19223
82
83
  utilities/typing.py,sha256=kVWK6ciV8T0MKxnFQcMSEr_XlRisspH5aBTTosMUh30,13872
83
84
  utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
84
85
  utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
@@ -88,7 +89,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
88
89
  utilities/whenever.py,sha256=tArX9unVEKhRYdvbUFa83e4hrzdtMKKCEN4QWTaYd8c,19524
89
90
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
90
91
  utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
91
- dycw_utilities-0.132.4.dist-info/METADATA,sha256=_AReviYgS7sJRlfoEv0VV19ABmMgXnFz2uWE0Rq3JNI,1522
92
- dycw_utilities-0.132.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
- dycw_utilities-0.132.4.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.132.4.dist-info/RECORD,,
92
+ dycw_utilities-0.133.0.dist-info/METADATA,sha256=8N2dkPiAUTKwY281OkaOGdvSP52CcijVTnjX9L63ctY,1522
93
+ dycw_utilities-0.133.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ dycw_utilities-0.133.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
95
+ dycw_utilities-0.133.0.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.132.4"
3
+ __version__ = "0.133.0"
utilities/arq.py ADDED
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from functools import wraps
5
+ from itertools import chain
6
+ from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast, override
7
+
8
+ from arq.constants import default_queue_name, expires_extra_ms
9
+ from arq.cron import cron
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Callable, Iterable, Sequence
13
+ from datetime import timezone
14
+
15
+ from arq.connections import ArqRedis, RedisSettings
16
+ from arq.cron import CronJob
17
+ from arq.jobs import Deserializer, Serializer
18
+ from arq.typing import (
19
+ OptionType,
20
+ SecondsTimedelta,
21
+ StartupShutdown,
22
+ WeekdayOptionType,
23
+ WorkerCoroutine,
24
+ )
25
+ from arq.worker import Function
26
+
27
+ from utilities.types import CallableCoroutine1, Coroutine1, StrMapping
28
+
29
+ _P = ParamSpec("_P")
30
+ _T = TypeVar("_T")
31
+
32
+
33
+ ##
34
+
35
+
36
+ def cron_raw(
37
+ coroutine: CallableCoroutine1[Any],
38
+ /,
39
+ *,
40
+ name: str | None = None,
41
+ month: OptionType = None,
42
+ day: OptionType = None,
43
+ weekday: WeekdayOptionType = None,
44
+ hour: OptionType = None,
45
+ minute: OptionType = None,
46
+ second: OptionType = 0,
47
+ microsecond: int = 123_456,
48
+ run_at_startup: bool = False,
49
+ unique: bool = True,
50
+ job_id: str | None = None,
51
+ timeout: SecondsTimedelta | None = None,
52
+ keep_result: float | None = 0,
53
+ keep_result_forever: bool | None = False,
54
+ max_tries: int | None = 1,
55
+ args: Iterable[Any] | None = None,
56
+ kwargs: StrMapping | None = None,
57
+ ) -> CronJob:
58
+ """Create a cron job with a raw coroutine function."""
59
+ lifted = _lift_cron(
60
+ coroutine, *(() if args is None else args), **({} if kwargs is None else kwargs)
61
+ )
62
+ return cron(
63
+ lifted,
64
+ name=name,
65
+ month=month,
66
+ day=day,
67
+ weekday=weekday,
68
+ hour=hour,
69
+ minute=minute,
70
+ second=second,
71
+ microsecond=microsecond,
72
+ run_at_startup=run_at_startup,
73
+ unique=unique,
74
+ job_id=job_id,
75
+ timeout=timeout,
76
+ keep_result=keep_result,
77
+ keep_result_forever=keep_result_forever,
78
+ max_tries=max_tries,
79
+ )
80
+
81
+
82
+ def _lift_cron(
83
+ func: Callable[_P, Coroutine1[_T]], *args: _P.args, **kwargs: _P.kwargs
84
+ ) -> WorkerCoroutine:
85
+ """Lift a coroutine function & call arg/kwargs for `cron`."""
86
+
87
+ @wraps(func)
88
+ async def wrapped(ctx: StrMapping, /) -> _T:
89
+ _ = ctx
90
+ return await func(*args, **kwargs)
91
+
92
+ return cast("Any", wrapped)
93
+
94
+
95
+ ##
96
+
97
+
98
+ class _WorkerMeta(type):
99
+ @override
100
+ def __new__(
101
+ mcs: type[_WorkerMeta],
102
+ name: str,
103
+ bases: tuple[type, ...],
104
+ namespace: dict[str, Any],
105
+ /,
106
+ ) -> type[Worker]:
107
+ cls = cast("type[Worker]", super().__new__(mcs, name, bases, namespace))
108
+ cls.functions = tuple(chain(cls.functions, map(cls._lift, cls.functions_raw)))
109
+ return cls
110
+
111
+ @classmethod
112
+ def _lift(cls, func: Callable[_P, Coroutine1[_T]]) -> WorkerCoroutine:
113
+ """Lift a coroutine function to accept the required `ctx` argument."""
114
+
115
+ @wraps(func)
116
+ async def wrapped(ctx: StrMapping, *args: _P.args, **kwargs: _P.kwargs) -> _T:
117
+ _ = ctx
118
+ return await func(*args, **kwargs)
119
+
120
+ return cast("Any", wrapped)
121
+
122
+
123
+ @dataclass(kw_only=True)
124
+ class Worker(metaclass=_WorkerMeta):
125
+ """Base class for all workers."""
126
+
127
+ functions: Sequence[Function | WorkerCoroutine] = ()
128
+ functions_raw: Sequence[CallableCoroutine1[Any]] = ()
129
+ queue_name: str | None = default_queue_name
130
+ cron_jobs: Sequence[CronJob] | None = None
131
+ redis_settings: RedisSettings | None = None
132
+ redis_pool: ArqRedis | None = None
133
+ burst: bool = False
134
+ on_startup: StartupShutdown | None = None
135
+ on_shutdown: StartupShutdown | None = None
136
+ on_job_start: StartupShutdown | None = None
137
+ on_job_end: StartupShutdown | None = None
138
+ after_job_end: StartupShutdown | None = None
139
+ handle_signals: bool = True
140
+ job_completion_wait: int = 0
141
+ max_jobs: int = 10
142
+ job_timeout: SecondsTimedelta = 300
143
+ keep_result: SecondsTimedelta = 3600
144
+ keep_result_forever: bool = False
145
+ poll_delay: SecondsTimedelta = 0.5
146
+ queue_read_limit: int | None = None
147
+ max_tries: int = 5
148
+ health_check_interval: SecondsTimedelta = 3600
149
+ health_check_key: str | None = None
150
+ ctx: dict[Any, Any] | None = None
151
+ retry_jobs: bool = True
152
+ allow_abort_jobs: bool = False
153
+ max_burst_jobs: int = -1
154
+ job_serializer: Serializer | None = None
155
+ job_deserializer: Deserializer | None = None
156
+ expires_extra_ms: int = expires_extra_ms
157
+ timezone: timezone | None = None
158
+ log_results: bool = True
159
+
160
+
161
+ __all__ = ["Worker", "cron"]
utilities/types.py CHANGED
@@ -72,12 +72,16 @@ type Coroutine1[_T] = Coroutine[Any, Any, _T]
72
72
  type MaybeAwaitable[_T] = _T | Awaitable[_T]
73
73
  type MaybeCallableEvent = MaybeCallable[Event]
74
74
  type MaybeCoroutine1[_T] = _T | Coroutine1[_T]
75
+ type CallableCoroutine1[_T] = Callable[..., Coroutine1[_T]]
75
76
 
76
77
 
77
78
  # callable
78
79
  TCallable = TypeVar("TCallable", bound=Callable[..., Any])
79
80
  TCallable1 = TypeVar("TCallable1", bound=Callable[..., Any])
80
81
  TCallable2 = TypeVar("TCallable2", bound=Callable[..., Any])
82
+ TCallableCoroutine1 = TypeVar(
83
+ "TCallableCoroutine1", bound=Callable[..., Coroutine1[Any]]
84
+ )
81
85
  TCallableMaybeCoroutine1None = TypeVar(
82
86
  "TCallableMaybeCoroutine1None", bound=Callable[..., MaybeCoroutine1[None]]
83
87
  )
@@ -292,6 +296,7 @@ type TimeZoneLike = (
292
296
 
293
297
 
294
298
  __all__ = [
299
+ "CallableCoroutine1",
295
300
  "Coroutine1",
296
301
  "Dataclass",
297
302
  "DateDeltaLike",
@@ -346,6 +351,7 @@ __all__ = [
346
351
  "TCallable",
347
352
  "TCallable1",
348
353
  "TCallable2",
354
+ "TCallableCoroutine1",
349
355
  "TCallableMaybeCoroutine1None",
350
356
  "TDataclass",
351
357
  "TEnum",