dycw-utilities 0.135.0__py3-none-any.whl → 0.178.1__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.

Potentially problematic release.


This version of dycw-utilities might be problematic. Click here for more details.

Files changed (97) hide show
  1. dycw_utilities-0.178.1.dist-info/METADATA +34 -0
  2. dycw_utilities-0.178.1.dist-info/RECORD +105 -0
  3. dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
  4. dycw_utilities-0.178.1.dist-info/entry_points.txt +4 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +13 -10
  7. utilities/asyncio.py +312 -787
  8. utilities/atomicwrites.py +18 -6
  9. utilities/atools.py +64 -4
  10. utilities/cachetools.py +9 -6
  11. utilities/click.py +195 -77
  12. utilities/concurrent.py +1 -1
  13. utilities/contextlib.py +216 -17
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +15 -28
  17. utilities/docker.py +387 -0
  18. utilities/enum.py +2 -2
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +28 -59
  21. utilities/fpdf2.py +2 -2
  22. utilities/functions.py +24 -269
  23. utilities/git.py +9 -30
  24. utilities/grp.py +28 -0
  25. utilities/gzip.py +31 -0
  26. utilities/http.py +3 -2
  27. utilities/hypothesis.py +513 -159
  28. utilities/importlib.py +17 -1
  29. utilities/inflect.py +12 -4
  30. utilities/iterables.py +33 -58
  31. utilities/jinja2.py +148 -0
  32. utilities/json.py +70 -0
  33. utilities/libcst.py +38 -17
  34. utilities/lightweight_charts.py +4 -7
  35. utilities/logging.py +136 -93
  36. utilities/math.py +8 -4
  37. utilities/more_itertools.py +43 -45
  38. utilities/operator.py +27 -27
  39. utilities/orjson.py +189 -36
  40. utilities/os.py +61 -4
  41. utilities/packaging.py +115 -0
  42. utilities/parse.py +8 -5
  43. utilities/pathlib.py +269 -40
  44. utilities/permissions.py +298 -0
  45. utilities/platform.py +7 -6
  46. utilities/polars.py +1205 -413
  47. utilities/polars_ols.py +1 -1
  48. utilities/postgres.py +408 -0
  49. utilities/pottery.py +43 -19
  50. utilities/pqdm.py +3 -3
  51. utilities/psutil.py +5 -57
  52. utilities/pwd.py +28 -0
  53. utilities/pydantic.py +4 -52
  54. utilities/pydantic_settings.py +240 -0
  55. utilities/pydantic_settings_sops.py +76 -0
  56. utilities/pyinstrument.py +7 -7
  57. utilities/pytest.py +104 -143
  58. utilities/pytest_plugins/__init__.py +1 -0
  59. utilities/pytest_plugins/pytest_randomly.py +23 -0
  60. utilities/pytest_plugins/pytest_regressions.py +56 -0
  61. utilities/pytest_regressions.py +26 -46
  62. utilities/random.py +11 -6
  63. utilities/re.py +1 -1
  64. utilities/redis.py +220 -343
  65. utilities/sentinel.py +10 -0
  66. utilities/shelve.py +4 -1
  67. utilities/shutil.py +25 -0
  68. utilities/slack_sdk.py +35 -104
  69. utilities/sqlalchemy.py +496 -471
  70. utilities/sqlalchemy_polars.py +29 -54
  71. utilities/string.py +2 -3
  72. utilities/subprocess.py +1977 -0
  73. utilities/tempfile.py +112 -4
  74. utilities/testbook.py +50 -0
  75. utilities/text.py +174 -42
  76. utilities/throttle.py +158 -0
  77. utilities/timer.py +2 -2
  78. utilities/traceback.py +70 -35
  79. utilities/types.py +102 -30
  80. utilities/typing.py +479 -19
  81. utilities/uuid.py +42 -5
  82. utilities/version.py +27 -26
  83. utilities/whenever.py +1559 -361
  84. utilities/zoneinfo.py +80 -22
  85. dycw_utilities-0.135.0.dist-info/METADATA +0 -39
  86. dycw_utilities-0.135.0.dist-info/RECORD +0 -96
  87. dycw_utilities-0.135.0.dist-info/WHEEL +0 -4
  88. dycw_utilities-0.135.0.dist-info/licenses/LICENSE +0 -21
  89. utilities/aiolimiter.py +0 -25
  90. utilities/arq.py +0 -216
  91. utilities/eventkit.py +0 -388
  92. utilities/luigi.py +0 -183
  93. utilities/period.py +0 -152
  94. utilities/pudb.py +0 -62
  95. utilities/python_dotenv.py +0 -101
  96. utilities/streamlit.py +0 -105
  97. utilities/typed_settings.py +0 -123
utilities/zoneinfo.py CHANGED
@@ -7,7 +7,8 @@ from zoneinfo import ZoneInfo
7
7
 
8
8
  from whenever import ZonedDateTime
9
9
 
10
- from utilities.tzlocal import LOCAL_TIME_ZONE
10
+ from utilities.types import TIME_ZONES
11
+ from utilities.tzlocal import LOCAL_TIME_ZONE, LOCAL_TIME_ZONE_NAME
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from utilities.types import TimeZone, TimeZoneLike
@@ -19,57 +20,114 @@ UTC = ZoneInfo("UTC")
19
20
  ##
20
21
 
21
22
 
22
- def ensure_time_zone(obj: TimeZoneLike, /) -> ZoneInfo:
23
- """Ensure the object is a time zone."""
23
+ def to_zone_info(obj: TimeZoneLike, /) -> ZoneInfo:
24
+ """Convert to a time-zone."""
24
25
  match obj:
25
26
  case ZoneInfo() as zone_info:
26
27
  return zone_info
27
- case ZonedDateTime() as datetime:
28
- return ZoneInfo(datetime.tz)
29
- case "local":
28
+ case ZonedDateTime() as date_time:
29
+ return ZoneInfo(date_time.tz)
30
+ case "local" | "localtime":
30
31
  return LOCAL_TIME_ZONE
31
32
  case str() as key:
32
33
  return ZoneInfo(key)
33
34
  case dt.tzinfo() as tzinfo:
34
35
  if tzinfo is dt.UTC:
35
36
  return UTC
36
- raise _EnsureTimeZoneInvalidTZInfoError(time_zone=obj)
37
- case dt.datetime() as datetime:
38
- if datetime.tzinfo is None:
39
- raise _EnsureTimeZonePlainDateTimeError(datetime=datetime)
40
- return ensure_time_zone(datetime.tzinfo)
41
- case _ as never:
37
+ raise _ToZoneInfoInvalidTZInfoError(time_zone=obj)
38
+ case dt.datetime() as date_time:
39
+ if date_time.tzinfo is None:
40
+ raise _ToZoneInfoPlainDateTimeError(date_time=date_time)
41
+ return to_zone_info(date_time.tzinfo)
42
+ case never:
42
43
  assert_never(never)
43
44
 
44
45
 
45
46
  @dataclass(kw_only=True, slots=True)
46
- class EnsureTimeZoneError(Exception): ...
47
+ class ToTimeZoneError(Exception): ...
47
48
 
48
49
 
49
50
  @dataclass(kw_only=True, slots=True)
50
- class _EnsureTimeZoneInvalidTZInfoError(EnsureTimeZoneError):
51
+ class _ToZoneInfoInvalidTZInfoError(ToTimeZoneError):
51
52
  time_zone: dt.tzinfo
52
53
 
53
54
  @override
54
55
  def __str__(self) -> str:
55
- return f"Unsupported time zone: {self.time_zone}"
56
+ return f"Invalid time-zone: {self.time_zone}"
56
57
 
57
58
 
58
59
  @dataclass(kw_only=True, slots=True)
59
- class _EnsureTimeZonePlainDateTimeError(EnsureTimeZoneError):
60
- datetime: dt.datetime
60
+ class _ToZoneInfoPlainDateTimeError(ToTimeZoneError):
61
+ date_time: dt.datetime
61
62
 
62
63
  @override
63
64
  def __str__(self) -> str:
64
- return f"Plain datetime: {self.datetime}"
65
+ return f"Plain date-time: {self.date_time}"
65
66
 
66
67
 
67
68
  ##
68
69
 
69
70
 
70
- def get_time_zone_name(time_zone: TimeZoneLike, /) -> TimeZone:
71
- """Get the name of a time zone."""
72
- return cast("TimeZone", ensure_time_zone(time_zone).key)
71
+ def to_time_zone_name(obj: TimeZoneLike, /) -> TimeZone:
72
+ """Convert to a time zone name."""
73
+ match obj:
74
+ case ZoneInfo() as zone_info:
75
+ return cast("TimeZone", zone_info.key)
76
+ case ZonedDateTime() as date_time:
77
+ return cast("TimeZone", date_time.tz)
78
+ case "local" | "localtime":
79
+ return LOCAL_TIME_ZONE_NAME
80
+ case str() as time_zone:
81
+ if time_zone in TIME_ZONES:
82
+ return time_zone
83
+ raise _ToTimeZoneNameInvalidKeyError(time_zone=time_zone)
84
+ case dt.tzinfo() as tzinfo:
85
+ if tzinfo is dt.UTC:
86
+ return cast("TimeZone", UTC.key)
87
+ raise _ToTimeZoneNameInvalidTZInfoError(time_zone=obj)
88
+ case dt.datetime() as date_time:
89
+ if date_time.tzinfo is None:
90
+ raise _ToTimeZoneNamePlainDateTimeError(date_time=date_time)
91
+ return to_time_zone_name(date_time.tzinfo)
92
+ case never:
93
+ assert_never(never)
94
+
95
+
96
+ @dataclass(kw_only=True, slots=True)
97
+ class ToTimeZoneNameError(Exception): ...
98
+
99
+
100
+ @dataclass(kw_only=True, slots=True)
101
+ class _ToTimeZoneNameInvalidKeyError(ToTimeZoneNameError):
102
+ time_zone: str
103
+
104
+ @override
105
+ def __str__(self) -> str:
106
+ return f"Invalid time-zone: {self.time_zone!r}"
107
+
108
+
109
+ @dataclass(kw_only=True, slots=True)
110
+ class _ToTimeZoneNameInvalidTZInfoError(ToTimeZoneNameError):
111
+ time_zone: dt.tzinfo
112
+
113
+ @override
114
+ def __str__(self) -> str:
115
+ return f"Invalid time-zone: {self.time_zone}"
116
+
117
+
118
+ @dataclass(kw_only=True, slots=True)
119
+ class _ToTimeZoneNamePlainDateTimeError(ToTimeZoneNameError):
120
+ date_time: dt.datetime
121
+
122
+ @override
123
+ def __str__(self) -> str:
124
+ return f"Plain date-time: {self.date_time}"
73
125
 
74
126
 
75
- __all__ = ["UTC", "EnsureTimeZoneError", "ensure_time_zone", "get_time_zone_name"]
127
+ __all__ = [
128
+ "UTC",
129
+ "ToTimeZoneError",
130
+ "ToTimeZoneNameError",
131
+ "to_time_zone_name",
132
+ "to_zone_info",
133
+ ]
@@ -1,39 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: dycw-utilities
3
- Version: 0.135.0
4
- Author-email: Derek Wan <d.wan@icloud.com>
5
- License-File: LICENSE
6
- Requires-Python: >=3.12
7
- Requires-Dist: atomicwrites<1.5,>=1.4.1
8
- Requires-Dist: typing-extensions<4.15,>=4.14.0
9
- Requires-Dist: tzlocal<5.4,>=5.3.1
10
- Requires-Dist: whenever<0.9,>=0.8.5
11
- Provides-Extra: logging
12
- Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
13
- Provides-Extra: test
14
- Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
15
- Requires-Dist: hypothesis<6.136,>=6.135.2; extra == 'test'
16
- Requires-Dist: pytest-asyncio<1.1,>=1.0.0; extra == 'test'
17
- Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
18
- Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
19
- Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.4; extra == 'test'
20
- Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
21
- Requires-Dist: pytest-regressions<2.9,>=2.8.0; extra == 'test'
22
- Requires-Dist: pytest-rerunfailures<16,>=15.1; extra == 'test'
23
- Requires-Dist: pytest-rng<1.1,>=1.0.0; extra == 'test'
24
- Requires-Dist: pytest-timeout<2.5,>=2.4.0; extra == 'test'
25
- Requires-Dist: pytest-xdist<3.8,>=3.7.0; extra == 'test'
26
- Requires-Dist: pytest<8.4,>=8.3.5; extra == 'test'
27
- Description-Content-Type: text/markdown
28
-
29
- [![PyPI version](https://badge.fury.io/py/dycw-utilities.svg)](https://badge.fury.io/py/dycw-utilities)
30
-
31
- # `dycw-utilities`
32
-
33
- [All the Python functions I don't want to write twice.](https://github.com/nvim-lua/plenary.nvim)
34
-
35
- ## Installation
36
-
37
- - `pip install dycw-utilities`
38
-
39
- or with [extras](https://github.com/dycw/python-utilities/blob/master/pyproject.toml).
@@ -1,96 +0,0 @@
1
- utilities/__init__.py,sha256=ZNWx_k_4ER3p4DVbJX8DEVVB_Qth1GiAAheIk1f0wkY,60
2
- utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
3
- utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
4
- utilities/arq.py,sha256=S-sfBfY-E1ErRKf4sSXt2YyCjKvu-pBlOECDfjBebRA,6399
5
- utilities/asyncio.py,sha256=qqoV2xXrZoOw1ck9yHOh8mxlnoh96Huwd5xGmJcu3fQ,38165
6
- utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
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
- utilities/contextlib.py,sha256=lpaLJBy3X0UGLWjM98jkQZZq8so4fRmoK-Bheq0uOW4,1027
12
- utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
13
- utilities/cryptography.py,sha256=_CiK_K6c_-uQuUhsUNjNjTL-nqxAh4_1zTfS11Xe120,972
14
- utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
15
- utilities/dataclasses.py,sha256=1-REGxtzCo2BbGkjLw5idjbTaiItpOiWvMNbVkJrFXM,32663
16
- utilities/enum.py,sha256=IEPMGiNJBcofGPuoGZErf4bMNSBE4SLs32nvm2r81h0,5791
17
- utilities/errors.py,sha256=nC7ZYtxxDBMfrTHtT_MByBfup_wfGQFRo3eDt-0ZPe8,1045
18
- utilities/eventkit.py,sha256=cV76NIHKDPyrhnZRuhjCXse5CQqgRGXZ7p7Gon4Mu2g,12646
19
- utilities/fastapi.py,sha256=E8T2J1-N_RbpkN4czthU6NPIxAZDzxy-k_WGJaxeJ48,2671
20
- utilities/fpdf2.py,sha256=PmPj8ugr_SlxFEpw-9OsI8--mteLQ4MaXv_Cbmf7XXs,1852
21
- utilities/functions.py,sha256=Teqm7ylOqEcBLHWhRwjKqouUfc6nv_6qO6fILpnOPyA,27937
22
- utilities/functools.py,sha256=I00ru2gQPakZw2SHVeKIKXfTv741655s6HI0lUoE0D4,1552
23
- utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
24
- utilities/git.py,sha256=oi7-_l5e9haSANSCvQw25ufYGoNahuUPHAZ6114s3JQ,1191
25
- utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
26
- utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
27
- utilities/hypothesis.py,sha256=_OFFvb8rVvMgXo04zaIFYw4p32QGrVHzJsR8pL3G8l4,36195
28
- utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
29
- utilities/inflect.py,sha256=DbqB5Q9FbRGJ1NbvEiZBirRMxCxgrz91zy5jCO9ZIs0,347
30
- utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
31
- utilities/iterables.py,sha256=wlcm0PS2fKG-H0r0k367NOLrryMbfXLlwzUeAmBSvv8,43392
32
- utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
33
- utilities/libcst.py,sha256=Jto5ppzRzsxn4AD32IS8n0lbgLYXwsVJB6EY8giNZyY,4974
34
- utilities/lightweight_charts.py,sha256=JrkrAZMo6JID2Eoc9QCc05Y_pK4l2zsApIhmii1z2Ig,2764
35
- utilities/logging.py,sha256=j0xS7bNdZcMAobWSRahpg_d7GWewd_99oXvexrjWm6k,17841
36
- utilities/luigi.py,sha256=wK7cB3y8NXeSa8d6r_yTKRmjMguNmIPmy52yg10vPaI,4774
37
- utilities/math.py,sha256=_6vrDyjtaqE_OFE-F2DNWrDG_J_kMl3nFAJsok9v_bY,26862
38
- utilities/memory_profiler.py,sha256=XzN56jDCa5aqXS_DxEjb_K4L6aIWh_5zyKi6OhcIxw0,853
39
- utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
40
- utilities/more_itertools.py,sha256=EX4P5JTc3p9fF3Ia3ZzPy9CEUe6OZcFqrrVplZ4pMaQ,10937
41
- utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
42
- utilities/operator.py,sha256=fZM2DBZdjtU8Lh9Z-eHC4ktNr0vO3JfX8hXgIYwdvBI,3826
43
- utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
44
- utilities/orjson.py,sha256=y5ynSGhQjX7vikfvsHh_AklLnH7gjvsSkIC3h5tnx98,36474
45
- utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
46
- utilities/parse.py,sha256=bCZW1bBfXM2j-yLGq0TGUshOnbL8V-U8amGu_OZTE_I,17907
47
- utilities/pathlib.py,sha256=jCFPZm4rBKylEva9wDVTwQlTTVKMepu92WrTpoGa438,3248
48
- utilities/period.py,sha256=QZsy2tCcoh0LGr70bkvlB5iWqEx6hdit3-iuaWJYXXc,4689
49
- utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
50
- utilities/platform.py,sha256=5uCKRf_ij7ukJDcbnNfhY2ay9fbrpiNLRO1t2QvcwqQ,2825
51
- utilities/polars.py,sha256=xCeB-pLkezOoQJvgxTdz2rNlaehdhek-HmDztXWn2j0,63266
52
- utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
53
- utilities/pottery.py,sha256=RN3XwOEsVAPXvEfsRPmn3ZSKgTzK_c182PNrtksq-bg,3429
54
- utilities/pqdm.py,sha256=BTsYPtbKQWwX-iXF4qCkfPG7DPxIB54J989n83bXrIo,3092
55
- utilities/psutil.py,sha256=0j4YxtVb8VjaaKKiHg6UEK95SUPkEcENgPtLgPJsNv0,3760
56
- utilities/pudb.py,sha256=Dte7P9J0VWrWTNYvwxZX6kgaEtdTt5ZnnmOAUTjltqU,1771
57
- utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- utilities/pydantic.py,sha256=CmxCi4sukeHM3JGjJ1Rbp8UAvcx4MZapLg10mFYJ-nk,1771
59
- utilities/pyinstrument.py,sha256=_Rfq6Gg4NKV2NF0dRYOpK2IRyyePxI7-3UmHIQLYrlQ,852
60
- utilities/pytest.py,sha256=vv5ZpePZS6pBxl5jY3XQR5SnAhgd4f0sSMyRsvHWg60,8086
61
- utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
62
- utilities/python_dotenv.py,sha256=dYooRYwqrvhSoZWuiVbCiKUWiS-M5b5yv2zDWGYPEvI,3209
63
- utilities/random.py,sha256=YWYzWxQDeyJRiuHGnO1OxF6dDucpq7qc1tH_ealwCRg,4130
64
- utilities/re.py,sha256=6qxeV0rQZaBDKWcB7apSBmxtg_XzoGY-EdegTkMn-ZY,4578
65
- utilities/redis.py,sha256=FuVyxF70BMnlnbQdvdv09Nj7xNuWmws4s94Bb-tIMNc,35415
66
- utilities/reprlib.py,sha256=ssYTcBW-TeRh3fhCJv57sopTZHF5FrPyyUg9yp5XBlo,3953
67
- utilities/scipy.py,sha256=wZJM7fEgBAkLSYYvSmsg5ac-QuwAI0BGqHVetw1_Hb0,947
68
- utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
69
- utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
70
- utilities/slack_sdk.py,sha256=RrB34gOj4TzBFh1vzMy6wL_ajzIG-2c9kiS6c6LzMFM,4233
71
- utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
72
- utilities/sqlalchemy.py,sha256=5Cx-ioYb099xDUDZuXrXp5tFU90HnCvu3jUu6wkGxrg,38001
73
- utilities/sqlalchemy_polars.py,sha256=5TwuUPORZ1Nz3qbI6L6pzeThtEv8Ws4aQW8cm3MG-v0,14293
74
- utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
75
- utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
76
- utilities/string.py,sha256=XmU-s04qIV_tODnKl2pQiwmHaxzgOqRKU-RyzdrfvSE,620
77
- utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
78
- utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
79
- utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
80
- utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
81
- utilities/traceback.py,sha256=i-790AQbTrDA8MiYyOcYPFpm48I558VR_kL_7x4ypfY,8503
82
- utilities/typed_settings.py,sha256=niNxgvVKrTy1c0j0D0Jp2tO3m231f4yJJ-38ni-xY3Q,3739
83
- utilities/types.py,sha256=DrFWZQqpkekHdnr2-G2eBaxGX8FoND6ON2XFRvhm8Jo,17177
84
- utilities/typing.py,sha256=Z-_XDaWyT_6wIo3qfNK-hvRlzxP2Jxa9PgXzm5rDYRA,13790
85
- utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
86
- utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
87
- utilities/uuid.py,sha256=32p7DGHGM2Btx6PcBvCZvERSWbpupMXqx6FppPoSoTU,612
88
- utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
89
- utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
90
- utilities/whenever.py,sha256=A-yoOqBqrcVD1yDINDsTFDw7dq9-zgUGn_f8CxVUQJs,23332
91
- utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
92
- utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
93
- dycw_utilities-0.135.0.dist-info/METADATA,sha256=USiapXRoQLG5Wl1CftVIV6nKma65HtH3nIoU7n8OMl8,1584
94
- dycw_utilities-0.135.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
95
- dycw_utilities-0.135.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
- dycw_utilities-0.135.0.dist-info/RECORD,,
@@ -1,4 +0,0 @@
1
- Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
3
- Root-Is-Purelib: true
4
- Tag: py3-none-any
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Derek Wan
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
utilities/aiolimiter.py DELETED
@@ -1,25 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from asyncio import get_running_loop
4
- from typing import TYPE_CHECKING
5
-
6
- from aiolimiter import AsyncLimiter
7
-
8
- if TYPE_CHECKING:
9
- from collections.abc import Hashable
10
-
11
- _LIMITERS: dict[tuple[int, Hashable], AsyncLimiter] = {}
12
-
13
-
14
- def get_async_limiter(key: Hashable, /, *, rate: float = 1.0) -> AsyncLimiter:
15
- """Get a loop-aware rate limiter."""
16
- id_ = id(get_running_loop())
17
- full = (id_, key)
18
- try:
19
- return _LIMITERS[full]
20
- except KeyError:
21
- limiter = _LIMITERS[full] = AsyncLimiter(1.0, time_period=rate)
22
- return limiter
23
-
24
-
25
- __all__ = ["get_async_limiter"]
utilities/arq.py DELETED
@@ -1,216 +0,0 @@
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, Self, cast, override
7
-
8
- from arq.constants import default_queue_name, expires_extra_ms
9
- from arq.cron import cron
10
-
11
- from utilities.dataclasses import replace_non_sentinel
12
- from utilities.sentinel import Sentinel, sentinel
13
-
14
- if TYPE_CHECKING:
15
- from collections.abc import Callable, Iterable, Sequence
16
- from datetime import datetime, timedelta, timezone
17
-
18
- from arq.connections import ArqRedis, RedisSettings
19
- from arq.cron import CronJob
20
- from arq.jobs import Deserializer, Job, Serializer
21
- from arq.typing import (
22
- OptionType,
23
- SecondsTimedelta,
24
- StartupShutdown,
25
- WeekdayOptionType,
26
- WorkerCoroutine,
27
- )
28
- from arq.worker import Function
29
-
30
- from utilities.types import Coro, StrMapping
31
-
32
-
33
- def cron_raw(
34
- coroutine: Callable[..., Coro[Any]],
35
- /,
36
- *,
37
- name: str | None = None,
38
- month: OptionType = None,
39
- day: OptionType = None,
40
- weekday: WeekdayOptionType = None,
41
- hour: OptionType = None,
42
- minute: OptionType = None,
43
- second: OptionType = 0,
44
- microsecond: int = 123_456,
45
- run_at_startup: bool = False,
46
- unique: bool = True,
47
- job_id: str | None = None,
48
- timeout: SecondsTimedelta | None = None,
49
- keep_result: float | None = 0,
50
- keep_result_forever: bool | None = False,
51
- max_tries: int | None = 1,
52
- args: Iterable[Any] | None = None,
53
- kwargs: StrMapping | None = None,
54
- ) -> CronJob:
55
- """Create a cron job with a raw coroutine function."""
56
- lifted = _lift_cron(
57
- coroutine, *(() if args is None else args), **({} if kwargs is None else kwargs)
58
- )
59
- return cron(
60
- lifted,
61
- name=name,
62
- month=month,
63
- day=day,
64
- weekday=weekday,
65
- hour=hour,
66
- minute=minute,
67
- second=second,
68
- microsecond=microsecond,
69
- run_at_startup=run_at_startup,
70
- unique=unique,
71
- job_id=job_id,
72
- timeout=timeout,
73
- keep_result=keep_result,
74
- keep_result_forever=keep_result_forever,
75
- max_tries=max_tries,
76
- )
77
-
78
-
79
- def _lift_cron[**P, T](
80
- func: Callable[P, Coro[T]], *args: P.args, **kwargs: P.kwargs
81
- ) -> WorkerCoroutine:
82
- """Lift a coroutine function & call arg/kwargs for `cron`."""
83
-
84
- @wraps(func)
85
- async def wrapped(ctx: StrMapping, /) -> T:
86
- _ = ctx
87
- return await func(*args, **kwargs)
88
-
89
- return cast("Any", wrapped)
90
-
91
-
92
- ##
93
-
94
-
95
- @dataclass(kw_only=True, slots=True)
96
- class _JobEnqueuer:
97
- """Enqueuer of jobs."""
98
-
99
- job_id: str | None = None
100
- queue_name: str | None = None
101
- defer_until: datetime | None = None
102
- defer_by: int | float | timedelta | None = None
103
- expires: int | float | timedelta | None = None
104
- job_try: int | None = None
105
-
106
- async def __call__[**P, T](
107
- self,
108
- redis: ArqRedis,
109
- function: Callable[P, Coro[T]],
110
- *args: P.args,
111
- **kwargs: P.kwargs,
112
- ) -> Job | None:
113
- return await redis.enqueue_job( # skipif-ci-and-not-linux
114
- function.__name__,
115
- *args,
116
- _job_id=self.job_id,
117
- _queue_name=self.queue_name,
118
- _defer_until=self.defer_until,
119
- _defer_by=self.defer_by,
120
- _expires=self.expires,
121
- _job_try=self.job_try,
122
- **kwargs,
123
- )
124
-
125
- def settings(
126
- self,
127
- *,
128
- job_id: str | None | Sentinel = sentinel,
129
- queue_name: str | None | Sentinel = sentinel,
130
- defer_until: datetime | None | Sentinel = sentinel,
131
- defer_by: float | timedelta | None | Sentinel = sentinel,
132
- expires: float | timedelta | None | Sentinel = sentinel,
133
- job_try: int | None | Sentinel = sentinel,
134
- ) -> Self:
135
- """Replace elements of the enqueuer."""
136
- return replace_non_sentinel( # skipif-ci-and-not-linux
137
- self,
138
- job_id=job_id,
139
- queue_name=queue_name,
140
- defer_until=defer_until,
141
- defer_by=defer_by,
142
- expires=expires,
143
- job_try=job_try,
144
- )
145
-
146
-
147
- job_enqueuer = _JobEnqueuer()
148
-
149
-
150
- ##
151
-
152
-
153
- class _WorkerMeta(type):
154
- @override
155
- def __new__(
156
- mcs: type[_WorkerMeta],
157
- name: str,
158
- bases: tuple[type, ...],
159
- namespace: dict[str, Any],
160
- /,
161
- ) -> type[Worker]:
162
- cls = cast("type[Worker]", super().__new__(mcs, name, bases, namespace))
163
- cls.functions = tuple(chain(cls.functions, map(cls._lift, cls.functions_raw)))
164
- return cls
165
-
166
- @classmethod
167
- def _lift[**P, T](cls, func: Callable[P, Coro[T]]) -> WorkerCoroutine:
168
- """Lift a coroutine function to accept the required `ctx` argument."""
169
-
170
- @wraps(func)
171
- async def wrapped(ctx: StrMapping, *args: P.args, **kwargs: P.kwargs) -> T:
172
- _ = ctx
173
- return await func(*args, **kwargs)
174
-
175
- return cast("Any", wrapped)
176
-
177
-
178
- @dataclass(kw_only=True)
179
- class Worker(metaclass=_WorkerMeta):
180
- """Base class for all workers."""
181
-
182
- functions: Sequence[Function | WorkerCoroutine] = ()
183
- functions_raw: Sequence[Callable[..., Coro[Any]]] = ()
184
- queue_name: str | None = default_queue_name
185
- cron_jobs: Sequence[CronJob] | None = None
186
- redis_settings: RedisSettings | None = None
187
- redis_pool: ArqRedis | None = None
188
- burst: bool = False
189
- on_startup: StartupShutdown | None = None
190
- on_shutdown: StartupShutdown | None = None
191
- on_job_start: StartupShutdown | None = None
192
- on_job_end: StartupShutdown | None = None
193
- after_job_end: StartupShutdown | None = None
194
- handle_signals: bool = True
195
- job_completion_wait: int = 0
196
- max_jobs: int = 10
197
- job_timeout: SecondsTimedelta = 300
198
- keep_result: SecondsTimedelta = 3600
199
- keep_result_forever: bool = False
200
- poll_delay: SecondsTimedelta = 0.5
201
- queue_read_limit: int | None = None
202
- max_tries: int = 5
203
- health_check_interval: SecondsTimedelta = 3600
204
- health_check_key: str | None = None
205
- ctx: dict[Any, Any] | None = None
206
- retry_jobs: bool = True
207
- allow_abort_jobs: bool = False
208
- max_burst_jobs: int = -1
209
- job_serializer: Serializer | None = None
210
- job_deserializer: Deserializer | None = None
211
- expires_extra_ms: int = expires_extra_ms
212
- timezone: timezone | None = None
213
- log_results: bool = True
214
-
215
-
216
- __all__ = ["Worker", "cron", "job_enqueuer"]