dycw-utilities 0.133.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.133.6
3
+ Version: 0.134.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,74 +1,74 @@
1
- utilities/__init__.py,sha256=zEbfqlJXaAM2jeU-uk4y12_DIVcHjGVmBbiIiG_zz0Q,60
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=objwVQZEmpX8XEtSdLB_Md_HY8VDEnT-_4Y_ujRy4NM,6498
5
- utilities/asyncio.py,sha256=USWMMrHqPVRr20vlIn_n5JLimyqa-5xLhuqDYWJed8A,37586
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=-bFGIrwYMFR7xl39j02DZMsO_u5x5_Ph7bRlBUFVYyw,1048
8
- utilities/cachetools.py,sha256=uBtEv4hD-TuCPX_cQy1lOpLF-QqfwnYGSf0o4Soqydc,2826
9
- utilities/click.py,sha256=2k7Ss2qKwYb1JCDB5IWpNf-B2WTyjKR1GxDW-Y6anPs,15736
10
- utilities/concurrent.py,sha256=s2scTEd2AhXVTW4hpASU2qxV_DiVLALfms55cCQzCvM,2886
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=iiC1wpGXWhaocIikzwBt8bbLWyImoUlOlcDZJGejaIg,33011
16
- utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
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=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
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=1GMHO3PJUPil9cpDNI656RN9aGWoJS2grzPFk3HWgJg,28267
22
- utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
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=LWEcjL0ip0cwawcIHhnWPVnB1OEH1GJnYEwYU6xtFpo,36259
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=cuebB4ivKlZuKm8S3PQIfjavn9h-5mBGmvYq4FpSxFg,43812
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=UAt4TDMtinLAN7sipX0jSvH-aZzHUTQbHB3Rwtbq994,4840
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=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
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=tBbjjKx8_Uv_TCjxhPwrGfAx_jRHtvLIZqXVWAsjzqA,8842
40
+ utilities/more_itertools.py,sha256=QCPzZvdsPbbd_ypPP06odqfyK79Vp4oQG9DQbaI205A,8644
41
41
  utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
42
- utilities/operator.py,sha256=DuiWdkmK0D-ddvFqOayDkazTQE1Ysvtl6-UiBN5gns8,3857
43
- utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
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=PK41rf1c9Wqv7h8f5R7H3_Lhq_gQZTUJD5tu3gMHVaU,3247
48
- utilities/period.py,sha256=opqpBevBGSGXbA7NYfRJjtthi1JPxdMaZ7QV3xosnTc,4774
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=BYTYniVSW9SrBWdmoTy8RqUgqBW4y07mlRPEWXPUyYg,63357
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=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
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=aP6OKowg2Md4rgQuQ5qTSF4bTbBuq7WtRb7zS3JSRGY,1841
57
+ utilities/pydantic.py,sha256=CmxCi4sukeHM3JGjJ1Rbp8UAvcx4MZapLg10mFYJ-nk,1771
58
58
  utilities/pyinstrument.py,sha256=_Rfq6Gg4NKV2NF0dRYOpK2IRyyePxI7-3UmHIQLYrlQ,852
59
- utilities/pytest.py,sha256=xSDybvkvdj7Ix-Tpc1INctKZV07qwrQvJlQonSimB7o,8273
59
+ utilities/pytest.py,sha256=vv5ZpePZS6pBxl5jY3XQR5SnAhgd4f0sSMyRsvHWg60,8086
60
60
  utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
61
- utilities/python_dotenv.py,sha256=edXsvHZhZnYeqfMfrsRRpj7_9eJI6uizh3xLx8Q9B3w,3228
62
- utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
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=u1nYu3ccGni8u3AFg5aXDdpW4ZmT6lGAHIk-wKeZPq4,35761
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=SsRMJD2HuPUjAFg-2JxOQ9IhKViu4f66cN5kt-C2a7M,4245
69
+ utilities/slack_sdk.py,sha256=RrB34gOj4TzBFh1vzMy6wL_ajzIG-2c9kiS6c6LzMFM,4233
70
70
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
71
- utilities/sqlalchemy.py,sha256=A4_31D58RECTshpYQWRJ6Rr3s5gm8YeySp-5Z4R7lRQ,38065
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=oYqRQ-G-DMOOHB6a4yP5-PJDVimLnbNkMnkOj_jUmFg,2474
79
+ utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
80
80
  utilities/traceback.py,sha256=i-790AQbTrDA8MiYyOcYPFpm48I558VR_kL_7x4ypfY,8503
81
- utilities/typed_settings.py,sha256=io3bhnglxO5FRNuTz1vpgbbGgvyj6VGJ5pytPRUeJo4,3769
82
- utilities/types.py,sha256=-ql33onw3mp3nS6ZBXyE-g1uYEdGhoTJQcYOfatSNGI,19360
83
- utilities/typing.py,sha256=kVWK6ciV8T0MKxnFQcMSEr_XlRisspH5aBTTosMUh30,13872
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=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
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.133.6.dist-info/METADATA,sha256=2kzp_mBpkFfRF9tQRbGoeEZN13gmZG4UpUS3DqB7RfE,1584
93
- dycw_utilities-0.133.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- dycw_utilities-0.133.6.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
95
- dycw_utilities-0.133.6.dist-info/RECORD,,
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
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.133.6"
3
+ __version__ = "0.134.0"
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, ParamSpec, Self, TypeVar, cast, override
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 CallableCoroutine1, Coroutine1, StrMapping
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: CallableCoroutine1[Any],
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[_P, Coroutine1[_T]], *args: _P.args, **kwargs: _P.kwargs
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, /) -> _T:
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[_P, Coroutine1[_T]],
117
- *args: _P.args,
118
- **kwargs: _P.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[_P, Coroutine1[_T]]) -> WorkerCoroutine:
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: _P.args, **kwargs: _P.kwargs) -> _T:
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[CallableCoroutine1[Any]] = ()
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[_T]):
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[_T]
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) -> _T:
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) -> _T:
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: _T) -> None:
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: _T) -> None:
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[_T]:
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[_T]:
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[_T] = []
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) -> _T:
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) -> _T:
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) -> _T:
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) -> _T:
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: _T) -> None:
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: _T) -> None:
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: _T) -> None:
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: _T) -> None:
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: _T) -> None:
147
+ def _put_left(self, item: T) -> None:
165
148
  self._queue.appendleft(item)
166
149
 
167
- def _get_right(self) -> _T:
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[[], _T], /) -> _T:
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[[], _T], /) -> _T:
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[[_T], None], /, *items: _T
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[[_T], None], item: _T, /
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[[_T], None], /, *items: _T
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[[_T], None], item: _T, /
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[_T],
262
+ coro: _CoroutineLike[T],
280
263
  *,
281
264
  name: str | None = None,
282
265
  context: Context | None = None,
283
- ) -> Task[_T]:
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[_T], /) -> Task[_T]:
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[_T], /
298
- ) -> _T:
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[_T], /) -> _T:
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(Generic[_T]):
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[_T] = field(
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[_T]:
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) -> _T:
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) -> _T:
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: _T) -> None:
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: _T) -> None:
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(PriorityQueue[tuple[TSupportsRichComparison, THashable]]):
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[THashable] = set()
822
+ self._set: set[U] = set()
838
823
 
839
824
  @override
840
- def _get(self) -> tuple[TSupportsRichComparison, THashable]:
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[TSupportsRichComparison, THashable]) -> None:
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[THashable]):
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[THashable] = set()
845
+ self._set: set[T] = set()
861
846
 
862
847
  @override
863
- def _get(self) -> THashable:
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: THashable) -> None:
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[_T], /, *, max_size: int | None = None) -> list[_T]:
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[_T], /, *, max_size: int | None = None) -> list[_T]:
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[_T] = []
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[_T], queue: Queue[_T], /) -> None:
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[_T], queue: Queue[_T], /) -> None:
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)