dycw-utilities 0.133.7__py3-none-any.whl → 0.134.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.
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/RECORD +39 -39
- utilities/__init__.py +1 -1
- utilities/arq.py +13 -20
- utilities/asyncio.py +59 -74
- utilities/atools.py +10 -13
- utilities/cachetools.py +11 -17
- utilities/click.py +31 -51
- utilities/concurrent.py +7 -10
- utilities/dataclasses.py +69 -91
- utilities/enum.py +24 -21
- utilities/eventkit.py +34 -48
- utilities/functions.py +133 -168
- utilities/functools.py +14 -18
- utilities/hypothesis.py +34 -44
- utilities/iterables.py +165 -179
- utilities/luigi.py +3 -15
- utilities/memory_profiler.py +11 -15
- utilities/more_itertools.py +85 -94
- utilities/operator.py +5 -7
- utilities/optuna.py +6 -6
- utilities/pathlib.py +1 -0
- utilities/period.py +7 -9
- utilities/polars.py +5 -16
- utilities/pqdm.py +7 -8
- utilities/pydantic.py +2 -4
- utilities/pytest.py +14 -23
- utilities/python_dotenv.py +5 -9
- utilities/random.py +2 -3
- utilities/redis.py +163 -181
- utilities/slack_sdk.py +2 -2
- utilities/sqlalchemy.py +4 -14
- utilities/timer.py +6 -0
- utilities/typed_settings.py +7 -10
- utilities/types.py +10 -94
- utilities/typing.py +32 -43
- utilities/uuid.py +1 -0
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.133.7.dist-info → dycw_utilities-0.134.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,74 +1,74 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=6Kft8t6oHMNZG-gaM4ZQ07SvvDvKryeqHULU_jPaWnE,60
|
2
2
|
utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
|
3
3
|
utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
|
4
|
-
utilities/arq.py,sha256=
|
5
|
-
utilities/asyncio.py,sha256=
|
4
|
+
utilities/arq.py,sha256=S-sfBfY-E1ErRKf4sSXt2YyCjKvu-pBlOECDfjBebRA,6399
|
5
|
+
utilities/asyncio.py,sha256=F12PF2z0letHzFhESXLlAYLTSV7MpTAGfW8FF3EWFmM,37403
|
6
6
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
7
|
-
utilities/atools.py,sha256
|
8
|
-
utilities/cachetools.py,sha256=
|
9
|
-
utilities/click.py,sha256=
|
10
|
-
utilities/concurrent.py,sha256=
|
7
|
+
utilities/atools.py,sha256=9im2g8OCf-Iynqa8bAv8N0Ycj9QvrJmGO7yLCZEdgII,986
|
8
|
+
utilities/cachetools.py,sha256=v1-9sXHLdOLiwmkq6NB0OUbxeKBuVVN6wmAWefWoaHI,2744
|
9
|
+
utilities/click.py,sha256=rXCbuBi6xqRK2-2e0hXQ2vYBWJzT9vPY6KkRIu73_uI,16049
|
10
|
+
utilities/concurrent.py,sha256=ZdhcNeBl1-HaAPY3h7bZ5ccuYdfdq2ATHplvZdnzlhk,2858
|
11
11
|
utilities/contextlib.py,sha256=lpaLJBy3X0UGLWjM98jkQZZq8so4fRmoK-Bheq0uOW4,1027
|
12
12
|
utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
13
13
|
utilities/cryptography.py,sha256=_CiK_K6c_-uQuUhsUNjNjTL-nqxAh4_1zTfS11Xe120,972
|
14
14
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
15
|
-
utilities/dataclasses.py,sha256=
|
16
|
-
utilities/enum.py,sha256=
|
15
|
+
utilities/dataclasses.py,sha256=1-REGxtzCo2BbGkjLw5idjbTaiItpOiWvMNbVkJrFXM,32663
|
16
|
+
utilities/enum.py,sha256=IEPMGiNJBcofGPuoGZErf4bMNSBE4SLs32nvm2r81h0,5791
|
17
17
|
utilities/errors.py,sha256=nC7ZYtxxDBMfrTHtT_MByBfup_wfGQFRo3eDt-0ZPe8,1045
|
18
|
-
utilities/eventkit.py,sha256=
|
18
|
+
utilities/eventkit.py,sha256=cV76NIHKDPyrhnZRuhjCXse5CQqgRGXZ7p7Gon4Mu2g,12646
|
19
19
|
utilities/fastapi.py,sha256=E8T2J1-N_RbpkN4czthU6NPIxAZDzxy-k_WGJaxeJ48,2671
|
20
20
|
utilities/fpdf2.py,sha256=PmPj8ugr_SlxFEpw-9OsI8--mteLQ4MaXv_Cbmf7XXs,1852
|
21
|
-
utilities/functions.py,sha256=
|
22
|
-
utilities/functools.py,sha256=
|
21
|
+
utilities/functions.py,sha256=Teqm7ylOqEcBLHWhRwjKqouUfc6nv_6qO6fILpnOPyA,27937
|
22
|
+
utilities/functools.py,sha256=mQv5yPMAy2CPVPnbxvRHhnDX3DnnyF1Mz5DIFWzWcro,1512
|
23
23
|
utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
24
24
|
utilities/git.py,sha256=oi7-_l5e9haSANSCvQw25ufYGoNahuUPHAZ6114s3JQ,1191
|
25
25
|
utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
|
26
26
|
utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
|
27
|
-
utilities/hypothesis.py,sha256=
|
27
|
+
utilities/hypothesis.py,sha256=_OFFvb8rVvMgXo04zaIFYw4p32QGrVHzJsR8pL3G8l4,36195
|
28
28
|
utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
29
29
|
utilities/inflect.py,sha256=DbqB5Q9FbRGJ1NbvEiZBirRMxCxgrz91zy5jCO9ZIs0,347
|
30
30
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
31
|
-
utilities/iterables.py,sha256=
|
31
|
+
utilities/iterables.py,sha256=wlcm0PS2fKG-H0r0k367NOLrryMbfXLlwzUeAmBSvv8,43392
|
32
32
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
33
33
|
utilities/libcst.py,sha256=Jto5ppzRzsxn4AD32IS8n0lbgLYXwsVJB6EY8giNZyY,4974
|
34
34
|
utilities/lightweight_charts.py,sha256=JrkrAZMo6JID2Eoc9QCc05Y_pK4l2zsApIhmii1z2Ig,2764
|
35
35
|
utilities/logging.py,sha256=j0xS7bNdZcMAobWSRahpg_d7GWewd_99oXvexrjWm6k,17841
|
36
|
-
utilities/luigi.py,sha256=
|
36
|
+
utilities/luigi.py,sha256=wK7cB3y8NXeSa8d6r_yTKRmjMguNmIPmy52yg10vPaI,4774
|
37
37
|
utilities/math.py,sha256=_6vrDyjtaqE_OFE-F2DNWrDG_J_kMl3nFAJsok9v_bY,26862
|
38
|
-
utilities/memory_profiler.py,sha256=
|
38
|
+
utilities/memory_profiler.py,sha256=XzN56jDCa5aqXS_DxEjb_K4L6aIWh_5zyKi6OhcIxw0,853
|
39
39
|
utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
|
40
|
-
utilities/more_itertools.py,sha256=
|
40
|
+
utilities/more_itertools.py,sha256=QCPzZvdsPbbd_ypPP06odqfyK79Vp4oQG9DQbaI205A,8644
|
41
41
|
utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
|
42
|
-
utilities/operator.py,sha256=
|
43
|
-
utilities/optuna.py,sha256=
|
42
|
+
utilities/operator.py,sha256=fZM2DBZdjtU8Lh9Z-eHC4ktNr0vO3JfX8hXgIYwdvBI,3826
|
43
|
+
utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
|
44
44
|
utilities/orjson.py,sha256=y5ynSGhQjX7vikfvsHh_AklLnH7gjvsSkIC3h5tnx98,36474
|
45
45
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
46
46
|
utilities/parse.py,sha256=PCy73pBmXgFzjxV54BC7TzHVhV3caDQvdvfXYqE_UUQ,17642
|
47
|
-
utilities/pathlib.py,sha256=
|
48
|
-
utilities/period.py,sha256=
|
47
|
+
utilities/pathlib.py,sha256=jCFPZm4rBKylEva9wDVTwQlTTVKMepu92WrTpoGa438,3248
|
48
|
+
utilities/period.py,sha256=QZsy2tCcoh0LGr70bkvlB5iWqEx6hdit3-iuaWJYXXc,4689
|
49
49
|
utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
|
50
50
|
utilities/platform.py,sha256=5uCKRf_ij7ukJDcbnNfhY2ay9fbrpiNLRO1t2QvcwqQ,2825
|
51
|
-
utilities/polars.py,sha256=
|
51
|
+
utilities/polars.py,sha256=xCeB-pLkezOoQJvgxTdz2rNlaehdhek-HmDztXWn2j0,63266
|
52
52
|
utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
|
53
53
|
utilities/pottery.py,sha256=RN3XwOEsVAPXvEfsRPmn3ZSKgTzK_c182PNrtksq-bg,3429
|
54
|
-
utilities/pqdm.py,sha256=
|
54
|
+
utilities/pqdm.py,sha256=BTsYPtbKQWwX-iXF4qCkfPG7DPxIB54J989n83bXrIo,3092
|
55
55
|
utilities/psutil.py,sha256=0j4YxtVb8VjaaKKiHg6UEK95SUPkEcENgPtLgPJsNv0,3760
|
56
56
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
utilities/pydantic.py,sha256=
|
57
|
+
utilities/pydantic.py,sha256=CmxCi4sukeHM3JGjJ1Rbp8UAvcx4MZapLg10mFYJ-nk,1771
|
58
58
|
utilities/pyinstrument.py,sha256=_Rfq6Gg4NKV2NF0dRYOpK2IRyyePxI7-3UmHIQLYrlQ,852
|
59
|
-
utilities/pytest.py,sha256=
|
59
|
+
utilities/pytest.py,sha256=vv5ZpePZS6pBxl5jY3XQR5SnAhgd4f0sSMyRsvHWg60,8086
|
60
60
|
utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
|
61
|
-
utilities/python_dotenv.py,sha256=
|
62
|
-
utilities/random.py,sha256=
|
61
|
+
utilities/python_dotenv.py,sha256=dYooRYwqrvhSoZWuiVbCiKUWiS-M5b5yv2zDWGYPEvI,3209
|
62
|
+
utilities/random.py,sha256=YWYzWxQDeyJRiuHGnO1OxF6dDucpq7qc1tH_ealwCRg,4130
|
63
63
|
utilities/re.py,sha256=6qxeV0rQZaBDKWcB7apSBmxtg_XzoGY-EdegTkMn-ZY,4578
|
64
|
-
utilities/redis.py,sha256=
|
64
|
+
utilities/redis.py,sha256=FuVyxF70BMnlnbQdvdv09Nj7xNuWmws4s94Bb-tIMNc,35415
|
65
65
|
utilities/reprlib.py,sha256=ssYTcBW-TeRh3fhCJv57sopTZHF5FrPyyUg9yp5XBlo,3953
|
66
66
|
utilities/scipy.py,sha256=wZJM7fEgBAkLSYYvSmsg5ac-QuwAI0BGqHVetw1_Hb0,947
|
67
67
|
utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
|
68
68
|
utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
|
69
|
-
utilities/slack_sdk.py,sha256=
|
69
|
+
utilities/slack_sdk.py,sha256=RrB34gOj4TzBFh1vzMy6wL_ajzIG-2c9kiS6c6LzMFM,4233
|
70
70
|
utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
|
71
|
-
utilities/sqlalchemy.py,sha256=
|
71
|
+
utilities/sqlalchemy.py,sha256=5Cx-ioYb099xDUDZuXrXp5tFU90HnCvu3jUu6wkGxrg,38001
|
72
72
|
utilities/sqlalchemy_polars.py,sha256=5TwuUPORZ1Nz3qbI6L6pzeThtEv8Ws4aQW8cm3MG-v0,14293
|
73
73
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
74
74
|
utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
|
@@ -76,20 +76,20 @@ utilities/string.py,sha256=XmU-s04qIV_tODnKl2pQiwmHaxzgOqRKU-RyzdrfvSE,620
|
|
76
76
|
utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
|
77
77
|
utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
|
78
78
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
79
|
-
utilities/timer.py,sha256=
|
79
|
+
utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
|
80
80
|
utilities/traceback.py,sha256=i-790AQbTrDA8MiYyOcYPFpm48I558VR_kL_7x4ypfY,8503
|
81
|
-
utilities/typed_settings.py,sha256=
|
82
|
-
utilities/types.py,sha256
|
83
|
-
utilities/typing.py,sha256=
|
81
|
+
utilities/typed_settings.py,sha256=niNxgvVKrTy1c0j0D0Jp2tO3m231f4yJJ-38ni-xY3Q,3739
|
82
|
+
utilities/types.py,sha256=DrFWZQqpkekHdnr2-G2eBaxGX8FoND6ON2XFRvhm8Jo,17177
|
83
|
+
utilities/typing.py,sha256=Z-_XDaWyT_6wIo3qfNK-hvRlzxP2Jxa9PgXzm5rDYRA,13790
|
84
84
|
utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
|
85
85
|
utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
|
86
|
-
utilities/uuid.py,sha256=
|
86
|
+
utilities/uuid.py,sha256=32p7DGHGM2Btx6PcBvCZvERSWbpupMXqx6FppPoSoTU,612
|
87
87
|
utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
|
88
88
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
89
89
|
utilities/whenever.py,sha256=A-yoOqBqrcVD1yDINDsTFDw7dq9-zgUGn_f8CxVUQJs,23332
|
90
90
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
91
91
|
utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
|
92
|
-
dycw_utilities-0.
|
93
|
-
dycw_utilities-0.
|
94
|
-
dycw_utilities-0.
|
95
|
-
dycw_utilities-0.
|
92
|
+
dycw_utilities-0.134.0.dist-info/METADATA,sha256=ge1UR3zk8DRZIcXfN3RY6DR9oh5TJ-nT-A2YWjlPz_4,1584
|
93
|
+
dycw_utilities-0.134.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
94
|
+
dycw_utilities-0.134.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
95
|
+
dycw_utilities-0.134.0.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/arq.py
CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from functools import wraps
|
5
5
|
from itertools import chain
|
6
|
-
from typing import TYPE_CHECKING, Any,
|
6
|
+
from typing import TYPE_CHECKING, Any, Self, cast, override
|
7
7
|
|
8
8
|
from arq.constants import default_queue_name, expires_extra_ms
|
9
9
|
from arq.cron import cron
|
@@ -27,18 +27,11 @@ if TYPE_CHECKING:
|
|
27
27
|
)
|
28
28
|
from arq.worker import Function
|
29
29
|
|
30
|
-
from utilities.types import
|
31
|
-
|
32
|
-
|
33
|
-
_P = ParamSpec("_P")
|
34
|
-
_T = TypeVar("_T")
|
35
|
-
|
36
|
-
|
37
|
-
##
|
30
|
+
from utilities.types import Coro, StrMapping
|
38
31
|
|
39
32
|
|
40
33
|
def cron_raw(
|
41
|
-
coroutine:
|
34
|
+
coroutine: Callable[..., Coro[Any]],
|
42
35
|
/,
|
43
36
|
*,
|
44
37
|
name: str | None = None,
|
@@ -83,13 +76,13 @@ def cron_raw(
|
|
83
76
|
)
|
84
77
|
|
85
78
|
|
86
|
-
def _lift_cron(
|
87
|
-
func: Callable[
|
79
|
+
def _lift_cron[**P, T](
|
80
|
+
func: Callable[P, Coro[T]], *args: P.args, **kwargs: P.kwargs
|
88
81
|
) -> WorkerCoroutine:
|
89
82
|
"""Lift a coroutine function & call arg/kwargs for `cron`."""
|
90
83
|
|
91
84
|
@wraps(func)
|
92
|
-
async def wrapped(ctx: StrMapping, /) ->
|
85
|
+
async def wrapped(ctx: StrMapping, /) -> T:
|
93
86
|
_ = ctx
|
94
87
|
return await func(*args, **kwargs)
|
95
88
|
|
@@ -110,12 +103,12 @@ class _JobEnqueuer:
|
|
110
103
|
expires: int | float | timedelta | None = None
|
111
104
|
job_try: int | None = None
|
112
105
|
|
113
|
-
async def __call__(
|
106
|
+
async def __call__[**P, T](
|
114
107
|
self,
|
115
108
|
redis: ArqRedis,
|
116
|
-
function: Callable[
|
117
|
-
*args:
|
118
|
-
**kwargs:
|
109
|
+
function: Callable[P, Coro[T]],
|
110
|
+
*args: P.args,
|
111
|
+
**kwargs: P.kwargs,
|
119
112
|
) -> Job | None:
|
120
113
|
return await redis.enqueue_job( # skipif-ci-and-not-linux
|
121
114
|
function.__name__,
|
@@ -171,11 +164,11 @@ class _WorkerMeta(type):
|
|
171
164
|
return cls
|
172
165
|
|
173
166
|
@classmethod
|
174
|
-
def _lift(cls, func: Callable[
|
167
|
+
def _lift[**P, T](cls, func: Callable[P, Coro[T]]) -> WorkerCoroutine:
|
175
168
|
"""Lift a coroutine function to accept the required `ctx` argument."""
|
176
169
|
|
177
170
|
@wraps(func)
|
178
|
-
async def wrapped(ctx: StrMapping, *args:
|
171
|
+
async def wrapped(ctx: StrMapping, *args: P.args, **kwargs: P.kwargs) -> T:
|
179
172
|
_ = ctx
|
180
173
|
return await func(*args, **kwargs)
|
181
174
|
|
@@ -187,7 +180,7 @@ class Worker(metaclass=_WorkerMeta):
|
|
187
180
|
"""Base class for all workers."""
|
188
181
|
|
189
182
|
functions: Sequence[Function | WorkerCoroutine] = ()
|
190
|
-
functions_raw: Sequence[
|
183
|
+
functions_raw: Sequence[Callable[..., Coro[Any]]] = ()
|
191
184
|
queue_name: str | None = default_queue_name
|
192
185
|
cron_jobs: Sequence[CronJob] | None = None
|
193
186
|
redis_settings: RedisSettings | None = None
|
utilities/asyncio.py
CHANGED
@@ -16,7 +16,7 @@ from asyncio import (
|
|
16
16
|
create_task,
|
17
17
|
sleep,
|
18
18
|
)
|
19
|
-
from collections.abc import Callable, Iterable, Iterator
|
19
|
+
from collections.abc import Callable, Hashable, Iterable, Iterator
|
20
20
|
from contextlib import (
|
21
21
|
AbstractAsyncContextManager,
|
22
22
|
AsyncExitStack,
|
@@ -30,17 +30,7 @@ from itertools import chain
|
|
30
30
|
from logging import DEBUG, Logger, getLogger
|
31
31
|
from subprocess import PIPE
|
32
32
|
from sys import stderr, stdout
|
33
|
-
from typing import
|
34
|
-
TYPE_CHECKING,
|
35
|
-
Any,
|
36
|
-
Generic,
|
37
|
-
Self,
|
38
|
-
TextIO,
|
39
|
-
TypeVar,
|
40
|
-
assert_never,
|
41
|
-
overload,
|
42
|
-
override,
|
43
|
-
)
|
33
|
+
from typing import TYPE_CHECKING, Any, Self, TextIO, assert_never, overload, override
|
44
34
|
|
45
35
|
from typing_extensions import deprecated
|
46
36
|
|
@@ -49,13 +39,7 @@ from utilities.errors import repr_error
|
|
49
39
|
from utilities.functions import ensure_int, ensure_not_none
|
50
40
|
from utilities.random import SYSTEM_RANDOM
|
51
41
|
from utilities.sentinel import Sentinel, sentinel
|
52
|
-
from utilities.types import
|
53
|
-
DateTimeRoundUnit,
|
54
|
-
MaybeCallableEvent,
|
55
|
-
MaybeType,
|
56
|
-
THashable,
|
57
|
-
TSupportsRichComparison,
|
58
|
-
)
|
42
|
+
from utilities.types import SupportsRichComparison
|
59
43
|
from utilities.whenever import SECOND, get_now
|
60
44
|
|
61
45
|
if TYPE_CHECKING:
|
@@ -69,11 +53,10 @@ if TYPE_CHECKING:
|
|
69
53
|
|
70
54
|
from whenever import TimeDelta, ZonedDateTime
|
71
55
|
|
72
|
-
|
73
|
-
_T = TypeVar("_T")
|
56
|
+
from utilities.types import DateTimeRoundUnit, MaybeCallableEvent, MaybeType
|
74
57
|
|
75
58
|
|
76
|
-
class EnhancedQueue(Queue[
|
59
|
+
class EnhancedQueue[T](Queue[T]):
|
77
60
|
"""An asynchronous deque."""
|
78
61
|
|
79
62
|
@override
|
@@ -82,39 +65,39 @@ class EnhancedQueue(Queue[_T]):
|
|
82
65
|
self._finished: Event
|
83
66
|
self._getters: deque[Any]
|
84
67
|
self._putters: deque[Any]
|
85
|
-
self._queue: deque[
|
68
|
+
self._queue: deque[T]
|
86
69
|
self._unfinished_tasks: int
|
87
70
|
|
88
71
|
@override
|
89
72
|
@deprecated("Use `get_left`/`get_right` instead")
|
90
|
-
async def get(self) ->
|
73
|
+
async def get(self) -> T:
|
91
74
|
raise RuntimeError # pragma: no cover
|
92
75
|
|
93
76
|
@override
|
94
77
|
@deprecated("Use `get_left_nowait`/`get_right_nowait` instead")
|
95
|
-
def get_nowait(self) ->
|
78
|
+
def get_nowait(self) -> T:
|
96
79
|
raise RuntimeError # pragma: no cover
|
97
80
|
|
98
81
|
@override
|
99
82
|
@deprecated("Use `put_left`/`put_right` instead")
|
100
|
-
async def put(self, item:
|
83
|
+
async def put(self, item: T) -> None:
|
101
84
|
raise RuntimeError(item) # pragma: no cover
|
102
85
|
|
103
86
|
@override
|
104
87
|
@deprecated("Use `put_left_nowait`/`put_right_nowait` instead")
|
105
|
-
def put_nowait(self, item:
|
88
|
+
def put_nowait(self, item: T) -> None:
|
106
89
|
raise RuntimeError(item) # pragma: no cover
|
107
90
|
|
108
91
|
# get all
|
109
92
|
|
110
|
-
async def get_all(self, *, reverse: bool = False) -> Sequence[
|
93
|
+
async def get_all(self, *, reverse: bool = False) -> Sequence[T]:
|
111
94
|
"""Remove and return all items from the queue."""
|
112
95
|
first = await (self.get_right() if reverse else self.get_left())
|
113
96
|
return list(chain([first], self.get_all_nowait(reverse=reverse)))
|
114
97
|
|
115
|
-
def get_all_nowait(self, *, reverse: bool = False) -> Sequence[
|
98
|
+
def get_all_nowait(self, *, reverse: bool = False) -> Sequence[T]:
|
116
99
|
"""Remove and return all items from the queue without blocking."""
|
117
|
-
items: Sequence[
|
100
|
+
items: Sequence[T] = []
|
118
101
|
while True:
|
119
102
|
try:
|
120
103
|
items.append(
|
@@ -125,49 +108,49 @@ class EnhancedQueue(Queue[_T]):
|
|
125
108
|
|
126
109
|
# get left/right
|
127
110
|
|
128
|
-
async def get_left(self) ->
|
111
|
+
async def get_left(self) -> T:
|
129
112
|
"""Remove and return an item from the start of the queue."""
|
130
113
|
return await self._get_left_or_right(self._get)
|
131
114
|
|
132
|
-
async def get_right(self) ->
|
115
|
+
async def get_right(self) -> T:
|
133
116
|
"""Remove and return an item from the end of the queue."""
|
134
117
|
return await self._get_left_or_right(self._get_right)
|
135
118
|
|
136
|
-
def get_left_nowait(self) ->
|
119
|
+
def get_left_nowait(self) -> T:
|
137
120
|
"""Remove and return an item from the start of the queue without blocking."""
|
138
121
|
return self._get_left_or_right_nowait(self._get)
|
139
122
|
|
140
|
-
def get_right_nowait(self) ->
|
123
|
+
def get_right_nowait(self) -> T:
|
141
124
|
"""Remove and return an item from the end of the queue without blocking."""
|
142
125
|
return self._get_left_or_right_nowait(self._get_right)
|
143
126
|
|
144
127
|
# put left/right
|
145
128
|
|
146
|
-
async def put_left(self, *items:
|
129
|
+
async def put_left(self, *items: T) -> None:
|
147
130
|
"""Put items into the queue at the start."""
|
148
131
|
return await self._put_left_or_right(self._put_left, *items)
|
149
132
|
|
150
|
-
async def put_right(self, *items:
|
133
|
+
async def put_right(self, *items: T) -> None:
|
151
134
|
"""Put items into the queue at the end."""
|
152
135
|
return await self._put_left_or_right(self._put, *items)
|
153
136
|
|
154
|
-
def put_left_nowait(self, *items:
|
137
|
+
def put_left_nowait(self, *items: T) -> None:
|
155
138
|
"""Put items into the queue at the start without blocking."""
|
156
139
|
self._put_left_or_right_nowait(self._put_left, *items)
|
157
140
|
|
158
|
-
def put_right_nowait(self, *items:
|
141
|
+
def put_right_nowait(self, *items: T) -> None:
|
159
142
|
"""Put items into the queue at the end without blocking."""
|
160
143
|
self._put_left_or_right_nowait(self._put, *items)
|
161
144
|
|
162
145
|
# private
|
163
146
|
|
164
|
-
def _put_left(self, item:
|
147
|
+
def _put_left(self, item: T) -> None:
|
165
148
|
self._queue.appendleft(item)
|
166
149
|
|
167
|
-
def _get_right(self) ->
|
150
|
+
def _get_right(self) -> T:
|
168
151
|
return self._queue.pop()
|
169
152
|
|
170
|
-
async def _get_left_or_right(self, getter_use: Callable[[],
|
153
|
+
async def _get_left_or_right(self, getter_use: Callable[[], T], /) -> T:
|
171
154
|
while self.empty(): # pragma: no cover
|
172
155
|
getter = self._get_loop().create_future() # pyright: ignore[reportAttributeAccessIssue]
|
173
156
|
self._getters.append(getter)
|
@@ -182,7 +165,7 @@ class EnhancedQueue(Queue[_T]):
|
|
182
165
|
raise
|
183
166
|
return getter_use()
|
184
167
|
|
185
|
-
def _get_left_or_right_nowait(self, getter: Callable[[],
|
168
|
+
def _get_left_or_right_nowait(self, getter: Callable[[], T], /) -> T:
|
186
169
|
if self.empty():
|
187
170
|
raise QueueEmpty
|
188
171
|
item = getter()
|
@@ -190,14 +173,14 @@ class EnhancedQueue(Queue[_T]):
|
|
190
173
|
return item
|
191
174
|
|
192
175
|
async def _put_left_or_right(
|
193
|
-
self, putter_use: Callable[[
|
176
|
+
self, putter_use: Callable[[T], None], /, *items: T
|
194
177
|
) -> None:
|
195
178
|
"""Put an item into the queue."""
|
196
179
|
for item in items:
|
197
180
|
await self._put_left_or_right_one(putter_use, item)
|
198
181
|
|
199
182
|
async def _put_left_or_right_one(
|
200
|
-
self, putter_use: Callable[[
|
183
|
+
self, putter_use: Callable[[T], None], item: T, /
|
201
184
|
) -> None:
|
202
185
|
"""Put an item into the queue."""
|
203
186
|
while self.full(): # pragma: no cover
|
@@ -215,13 +198,13 @@ class EnhancedQueue(Queue[_T]):
|
|
215
198
|
return putter_use(item)
|
216
199
|
|
217
200
|
def _put_left_or_right_nowait(
|
218
|
-
self, putter: Callable[[
|
201
|
+
self, putter: Callable[[T], None], /, *items: T
|
219
202
|
) -> None:
|
220
203
|
for item in items:
|
221
204
|
self._put_left_or_right_nowait_one(putter, item)
|
222
205
|
|
223
206
|
def _put_left_or_right_nowait_one(
|
224
|
-
self, putter: Callable[[
|
207
|
+
self, putter: Callable[[T], None], item: T, /
|
225
208
|
) -> None:
|
226
209
|
if self.full(): # pragma: no cover
|
227
210
|
raise QueueFull
|
@@ -274,13 +257,13 @@ class EnhancedTaskGroup(TaskGroup):
|
|
274
257
|
_ = await super().__aexit__(et, exc, tb)
|
275
258
|
|
276
259
|
@override
|
277
|
-
def create_task(
|
260
|
+
def create_task[T](
|
278
261
|
self,
|
279
|
-
coro: _CoroutineLike[
|
262
|
+
coro: _CoroutineLike[T],
|
280
263
|
*,
|
281
264
|
name: str | None = None,
|
282
265
|
context: Context | None = None,
|
283
|
-
) -> Task[
|
266
|
+
) -> Task[T]:
|
284
267
|
if self._semaphore is None:
|
285
268
|
coroutine = coro
|
286
269
|
else:
|
@@ -288,18 +271,18 @@ class EnhancedTaskGroup(TaskGroup):
|
|
288
271
|
coroutine = self._wrap_with_timeout(coroutine)
|
289
272
|
return super().create_task(coroutine, name=name, context=context)
|
290
273
|
|
291
|
-
def create_task_context(self, cm: AbstractAsyncContextManager[
|
274
|
+
def create_task_context[T](self, cm: AbstractAsyncContextManager[T], /) -> Task[T]:
|
292
275
|
"""Have the TaskGroup start an asynchronous context manager."""
|
293
276
|
_ = self._stack.push_async_callback(cm.__aexit__, None, None, None)
|
294
277
|
return self.create_task(cm.__aenter__())
|
295
278
|
|
296
|
-
async def _wrap_with_semaphore(
|
297
|
-
self, semaphore: Semaphore, coroutine: _CoroutineLike[
|
298
|
-
) ->
|
279
|
+
async def _wrap_with_semaphore[T](
|
280
|
+
self, semaphore: Semaphore, coroutine: _CoroutineLike[T], /
|
281
|
+
) -> T:
|
299
282
|
async with semaphore:
|
300
283
|
return await coroutine
|
301
284
|
|
302
|
-
async def _wrap_with_timeout(self, coroutine: _CoroutineLike[
|
285
|
+
async def _wrap_with_timeout[T](self, coroutine: _CoroutineLike[T], /) -> T:
|
303
286
|
async with timeout_td(self._timeout, error=self._error):
|
304
287
|
return await coroutine
|
305
288
|
|
@@ -321,7 +304,7 @@ class _LooperNoTaskError(LooperError):
|
|
321
304
|
|
322
305
|
|
323
306
|
@dataclass(kw_only=True, unsafe_hash=True)
|
324
|
-
class Looper
|
307
|
+
class Looper[T]:
|
325
308
|
"""A looper of a core coroutine, handling errors."""
|
326
309
|
|
327
310
|
auto_start: bool = field(default=False, repr=False)
|
@@ -362,7 +345,7 @@ class Looper(Generic[_T]):
|
|
362
345
|
# internal objects
|
363
346
|
_lock: Lock = field(default_factory=Lock, init=False, repr=False, hash=False)
|
364
347
|
_logger: Logger = field(init=False, repr=False, hash=False)
|
365
|
-
_queue: EnhancedQueue[
|
348
|
+
_queue: EnhancedQueue[T] = field(
|
366
349
|
default_factory=EnhancedQueue, init=False, repr=False, hash=False
|
367
350
|
)
|
368
351
|
_stack: AsyncExitStack = field(
|
@@ -448,15 +431,15 @@ class Looper(Generic[_T]):
|
|
448
431
|
"""Check if the queue is empty."""
|
449
432
|
return self._queue.empty()
|
450
433
|
|
451
|
-
def get_all_nowait(self, *, reverse: bool = False) -> Sequence[
|
434
|
+
def get_all_nowait(self, *, reverse: bool = False) -> Sequence[T]:
|
452
435
|
"""Remove and return all items from the queue without blocking."""
|
453
436
|
return self._queue.get_all_nowait(reverse=reverse)
|
454
437
|
|
455
|
-
def get_left_nowait(self) ->
|
438
|
+
def get_left_nowait(self) -> T:
|
456
439
|
"""Remove and return an item from the start of the queue without blocking."""
|
457
440
|
return self._queue.get_left_nowait()
|
458
441
|
|
459
|
-
def get_right_nowait(self) ->
|
442
|
+
def get_right_nowait(self) -> T:
|
460
443
|
"""Remove and return an item from the end of the queue without blocking."""
|
461
444
|
return self._queue.get_right_nowait()
|
462
445
|
|
@@ -514,11 +497,11 @@ class Looper(Generic[_T]):
|
|
514
497
|
async def _initialize_core(self) -> None:
|
515
498
|
"""Core part of initializing the looper."""
|
516
499
|
|
517
|
-
def put_left_nowait(self, *items:
|
500
|
+
def put_left_nowait(self, *items: T) -> None:
|
518
501
|
"""Put items into the queue at the start without blocking."""
|
519
502
|
self._queue.put_left_nowait(*items)
|
520
503
|
|
521
|
-
def put_right_nowait(self, *items:
|
504
|
+
def put_right_nowait(self, *items: T) -> None:
|
522
505
|
"""Put items into the queue at the end without blocking."""
|
523
506
|
self._queue.put_right_nowait(*items)
|
524
507
|
|
@@ -828,45 +811,47 @@ class _LooperStats:
|
|
828
811
|
##
|
829
812
|
|
830
813
|
|
831
|
-
class UniquePriorityQueue
|
814
|
+
class UniquePriorityQueue[T: SupportsRichComparison, U: Hashable](
|
815
|
+
PriorityQueue[tuple[T, U]]
|
816
|
+
):
|
832
817
|
"""Priority queue with unique tasks."""
|
833
818
|
|
834
819
|
@override
|
835
820
|
def __init__(self, maxsize: int = 0) -> None:
|
836
821
|
super().__init__(maxsize)
|
837
|
-
self._set: set[
|
822
|
+
self._set: set[U] = set()
|
838
823
|
|
839
824
|
@override
|
840
|
-
def _get(self) -> tuple[
|
825
|
+
def _get(self) -> tuple[T, U]:
|
841
826
|
item = super()._get()
|
842
827
|
_, value = item
|
843
828
|
self._set.remove(value)
|
844
829
|
return item
|
845
830
|
|
846
831
|
@override
|
847
|
-
def _put(self, item: tuple[
|
832
|
+
def _put(self, item: tuple[T, U]) -> None:
|
848
833
|
_, value = item
|
849
834
|
if value not in self._set:
|
850
835
|
super()._put(item)
|
851
836
|
self._set.add(value)
|
852
837
|
|
853
838
|
|
854
|
-
class UniqueQueue(Queue[
|
839
|
+
class UniqueQueue[T: Hashable](Queue[T]):
|
855
840
|
"""Queue with unique tasks."""
|
856
841
|
|
857
842
|
@override
|
858
843
|
def __init__(self, maxsize: int = 0) -> None:
|
859
844
|
super().__init__(maxsize)
|
860
|
-
self._set: set[
|
845
|
+
self._set: set[T] = set()
|
861
846
|
|
862
847
|
@override
|
863
|
-
def _get(self) ->
|
848
|
+
def _get(self) -> T:
|
864
849
|
item = super()._get()
|
865
850
|
self._set.remove(item)
|
866
851
|
return item
|
867
852
|
|
868
853
|
@override
|
869
|
-
def _put(self, item:
|
854
|
+
def _put(self, item: T) -> None:
|
870
855
|
if item not in self._set:
|
871
856
|
super()._put(item)
|
872
857
|
self._set.add(item)
|
@@ -903,7 +888,7 @@ def get_event(
|
|
903
888
|
##
|
904
889
|
|
905
890
|
|
906
|
-
async def get_items(queue: Queue[
|
891
|
+
async def get_items[T](queue: Queue[T], /, *, max_size: int | None = None) -> list[T]:
|
907
892
|
"""Get items from a queue; if empty then wait."""
|
908
893
|
try:
|
909
894
|
items = [await queue.get()]
|
@@ -916,9 +901,9 @@ async def get_items(queue: Queue[_T], /, *, max_size: int | None = None) -> list
|
|
916
901
|
return items
|
917
902
|
|
918
903
|
|
919
|
-
def get_items_nowait(queue: Queue[
|
904
|
+
def get_items_nowait[T](queue: Queue[T], /, *, max_size: int | None = None) -> list[T]:
|
920
905
|
"""Get items from a queue; no waiting."""
|
921
|
-
items: list[
|
906
|
+
items: list[T] = []
|
922
907
|
if max_size is None:
|
923
908
|
while True:
|
924
909
|
try:
|
@@ -937,13 +922,13 @@ def get_items_nowait(queue: Queue[_T], /, *, max_size: int | None = None) -> lis
|
|
937
922
|
##
|
938
923
|
|
939
924
|
|
940
|
-
async def put_items(items: Iterable[
|
925
|
+
async def put_items[T](items: Iterable[T], queue: Queue[T], /) -> None:
|
941
926
|
"""Put items into a queue; if full then wait."""
|
942
927
|
for item in items:
|
943
928
|
await queue.put(item)
|
944
929
|
|
945
930
|
|
946
|
-
def put_items_nowait(items: Iterable[
|
931
|
+
def put_items_nowait[T](items: Iterable[T], queue: Queue[T], /) -> None:
|
947
932
|
"""Put items into a queue; no waiting."""
|
948
933
|
for item in items:
|
949
934
|
queue.put_nowait(item)
|