dycw-utilities 0.131.1__py3-none-any.whl → 0.131.3__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.131.1.dist-info → dycw_utilities-0.131.3.dist-info}/METADATA +4 -45
- {dycw_utilities-0.131.1.dist-info → dycw_utilities-0.131.3.dist-info}/RECORD +22 -21
- utilities/__init__.py +1 -1
- utilities/altair.py +1 -4
- utilities/fastapi.py +2 -3
- utilities/fpdf2.py +2 -2
- utilities/hypothesis.py +160 -3
- utilities/lightweight_charts.py +1 -2
- utilities/logging.py +3 -5
- utilities/orjson.py +0 -2
- utilities/pickle.py +2 -2
- utilities/pydantic.py +2 -2
- utilities/pyinstrument.py +1 -2
- utilities/pytest.py +1 -2
- utilities/traceback.py +3 -7
- utilities/typing.py +1 -1
- utilities/tzlocal.py +6 -1
- utilities/whenever.py +1 -64
- utilities/whenever2.py +121 -0
- utilities/zoneinfo.py +2 -2
- {dycw_utilities-0.131.1.dist-info → dycw_utilities-0.131.3.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.1.dist-info → dycw_utilities-0.131.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,15 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.131.
|
3
|
+
Version: 0.131.3
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
7
|
+
Requires-Dist: atomicwrites<1.5,>=1.4.1
|
7
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
|
8
11
|
Provides-Extra: logging
|
9
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'logging'
|
10
12
|
Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
|
11
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'logging'
|
12
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'logging'
|
13
13
|
Provides-Extra: test
|
14
14
|
Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
|
15
15
|
Requires-Dist: hypothesis<6.136,>=6.135.2; extra == 'test'
|
@@ -28,14 +28,11 @@ Provides-Extra: zzz-test-aiolimiter
|
|
28
28
|
Requires-Dist: aiolimiter<1.3,>=1.2.1; extra == 'zzz-test-aiolimiter'
|
29
29
|
Provides-Extra: zzz-test-altair
|
30
30
|
Requires-Dist: altair<5.6,>=5.5.0; extra == 'zzz-test-altair'
|
31
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-altair'
|
32
31
|
Requires-Dist: img2pdf<0.7,>=0.6.0; extra == 'zzz-test-altair'
|
33
32
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-altair'
|
34
33
|
Requires-Dist: vl-convert-python<1.9,>=1.8.0; extra == 'zzz-test-altair'
|
35
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-altair'
|
36
34
|
Provides-Extra: zzz-test-asyncio
|
37
35
|
Provides-Extra: zzz-test-atomicwrites
|
38
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-atomicwrites'
|
39
36
|
Provides-Extra: zzz-test-atools
|
40
37
|
Requires-Dist: atools<0.15,>=0.14.2; extra == 'zzz-test-atools'
|
41
38
|
Provides-Extra: zzz-test-cachetools
|
@@ -43,7 +40,6 @@ Requires-Dist: cachetools<5.6,>=5.5.2; extra == 'zzz-test-cachetools'
|
|
43
40
|
Provides-Extra: zzz-test-click
|
44
41
|
Requires-Dist: click<8.3,>=8.2.1; extra == 'zzz-test-click'
|
45
42
|
Requires-Dist: sqlalchemy<2.1,>=2.0.41; extra == 'zzz-test-click'
|
46
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-click'
|
47
43
|
Provides-Extra: zzz-test-contextlib
|
48
44
|
Provides-Extra: zzz-test-contextvars
|
49
45
|
Provides-Extra: zzz-test-cryptography
|
@@ -53,10 +49,7 @@ Requires-Dist: cvxpy<1.7,>=1.6.5; extra == 'zzz-test-cvxpy'
|
|
53
49
|
Provides-Extra: zzz-test-dataclasses
|
54
50
|
Requires-Dist: orjson<3.11,>=3.10.15; extra == 'zzz-test-dataclasses'
|
55
51
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-dataclasses'
|
56
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-dataclasses'
|
57
52
|
Provides-Extra: zzz-test-datetime
|
58
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-datetime'
|
59
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-datetime'
|
60
53
|
Provides-Extra: zzz-test-enum
|
61
54
|
Provides-Extra: zzz-test-errors
|
62
55
|
Provides-Extra: zzz-test-eventkit
|
@@ -67,7 +60,6 @@ Requires-Dist: httpx<0.29,>=0.28.1; extra == 'zzz-test-fastapi'
|
|
67
60
|
Requires-Dist: uvicorn<0.35,>=0.34.1; extra == 'zzz-test-fastapi'
|
68
61
|
Provides-Extra: zzz-test-fpdf2
|
69
62
|
Requires-Dist: fpdf2<2.9,>=2.8.3; extra == 'zzz-test-fpdf2'
|
70
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-fpdf2'
|
71
63
|
Provides-Extra: zzz-test-functions
|
72
64
|
Provides-Extra: zzz-test-functools
|
73
65
|
Provides-Extra: zzz-test-getpass
|
@@ -75,11 +67,8 @@ Provides-Extra: zzz-test-git
|
|
75
67
|
Provides-Extra: zzz-test-hashlib
|
76
68
|
Requires-Dist: orjson<3.11,>=3.10.15; extra == 'zzz-test-hashlib'
|
77
69
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-hashlib'
|
78
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-hashlib'
|
79
70
|
Provides-Extra: zzz-test-http
|
80
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-http'
|
81
71
|
Requires-Dist: orjson<3.11,>=3.10.18; extra == 'zzz-test-http'
|
82
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-http'
|
83
72
|
Provides-Extra: zzz-test-hypothesis
|
84
73
|
Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
|
85
74
|
Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
|
@@ -91,27 +80,20 @@ Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
|
|
91
80
|
Requires-Dist: redis<6.3,>=6.2.0; extra == 'zzz-test-hypothesis'
|
92
81
|
Requires-Dist: sqlalchemy<2.1,>=2.0.41; extra == 'zzz-test-hypothesis'
|
93
82
|
Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-hypothesis'
|
94
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-hypothesis'
|
95
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-hypothesis'
|
96
83
|
Provides-Extra: zzz-test-ipython
|
97
84
|
Requires-Dist: ipython<9.1,>=9.0.1; extra == 'zzz-test-ipython'
|
98
85
|
Provides-Extra: zzz-test-iterables
|
99
86
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-iterables'
|
100
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-iterables'
|
101
87
|
Provides-Extra: zzz-test-jupyter
|
102
88
|
Requires-Dist: jupyterlab<4.3,>=4.2.0; extra == 'zzz-test-jupyter'
|
103
89
|
Requires-Dist: pandas<2.4,>=2.3.0; extra == 'zzz-test-jupyter'
|
104
90
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-jupyter'
|
105
91
|
Provides-Extra: zzz-test-logging
|
106
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-logging'
|
107
92
|
Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'zzz-test-logging'
|
108
93
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-logging'
|
109
94
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-logging'
|
110
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-logging'
|
111
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-logging'
|
112
95
|
Provides-Extra: zzz-test-luigi
|
113
96
|
Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-luigi'
|
114
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-luigi'
|
115
97
|
Provides-Extra: zzz-test-math
|
116
98
|
Requires-Dist: numpy<2.4,>=2.3.0; extra == 'zzz-test-math'
|
117
99
|
Provides-Extra: zzz-test-memory-profiler
|
@@ -123,43 +105,32 @@ Provides-Extra: zzz-test-numpy
|
|
123
105
|
Requires-Dist: numpy<2.4,>=2.3.0; extra == 'zzz-test-numpy'
|
124
106
|
Provides-Extra: zzz-test-operator
|
125
107
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-operator'
|
126
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-operator'
|
127
108
|
Provides-Extra: zzz-test-optuna
|
128
109
|
Requires-Dist: optuna<4.4,>=4.3.0; extra == 'zzz-test-optuna'
|
129
110
|
Provides-Extra: zzz-test-orjson
|
130
111
|
Requires-Dist: orjson<3.11,>=3.10.15; extra == 'zzz-test-orjson'
|
131
112
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-orjson'
|
132
113
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-orjson'
|
133
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-orjson'
|
134
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-orjson'
|
135
114
|
Provides-Extra: zzz-test-os
|
136
115
|
Provides-Extra: zzz-test-pathlib
|
137
116
|
Provides-Extra: zzz-test-pickle
|
138
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-pickle'
|
139
117
|
Provides-Extra: zzz-test-platform
|
140
118
|
Provides-Extra: zzz-test-polars
|
141
119
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-polars'
|
142
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-polars'
|
143
120
|
Provides-Extra: zzz-test-pqdm
|
144
121
|
Requires-Dist: pqdm<0.3,>=0.2.0; extra == 'zzz-test-pqdm'
|
145
122
|
Provides-Extra: zzz-test-pydantic
|
146
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-pydantic'
|
147
123
|
Requires-Dist: pydantic<2.12,>=2.11.4; extra == 'zzz-test-pydantic'
|
148
124
|
Provides-Extra: zzz-test-pyinstrument
|
149
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-pyinstrument'
|
150
125
|
Requires-Dist: pyinstrument<5.1,>=5.0.2; extra == 'zzz-test-pyinstrument'
|
151
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-pyinstrument'
|
152
126
|
Provides-Extra: zzz-test-pyrsistent
|
153
127
|
Requires-Dist: pyrsistent<0.21,>=0.20.0; extra == 'zzz-test-pyrsistent'
|
154
128
|
Provides-Extra: zzz-test-pytest
|
155
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-pytest'
|
156
129
|
Requires-Dist: orjson<3.11,>=3.10.18; extra == 'zzz-test-pytest'
|
157
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-pytest'
|
158
130
|
Provides-Extra: zzz-test-pytest-regressions
|
159
131
|
Requires-Dist: pytest-regressions<2.9,>=2.8.0; extra == 'zzz-test-pytest-regressions'
|
160
132
|
Provides-Extra: zzz-test-python-dotenv
|
161
133
|
Requires-Dist: python-dotenv<1.2,>=1.1.0; extra == 'zzz-test-python-dotenv'
|
162
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-python-dotenv'
|
163
134
|
Provides-Extra: zzz-test-random
|
164
135
|
Provides-Extra: zzz-test-re
|
165
136
|
Provides-Extra: zzz-test-redis
|
@@ -168,8 +139,6 @@ Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-redis'
|
|
168
139
|
Requires-Dist: redis<6.3,>=6.2.0; extra == 'zzz-test-redis'
|
169
140
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-redis'
|
170
141
|
Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-redis'
|
171
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-redis'
|
172
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-redis'
|
173
142
|
Provides-Extra: zzz-test-rich
|
174
143
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-rich'
|
175
144
|
Provides-Extra: zzz-test-scipy
|
@@ -195,15 +164,11 @@ Requires-Dist: nest-asyncio<1.7,>=1.6.0; extra == 'zzz-test-sqlalchemy-polars'
|
|
195
164
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-sqlalchemy-polars'
|
196
165
|
Requires-Dist: sqlalchemy<2.1,>=2.0.41; extra == 'zzz-test-sqlalchemy-polars'
|
197
166
|
Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-sqlalchemy-polars'
|
198
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-sqlalchemy-polars'
|
199
167
|
Provides-Extra: zzz-test-streamlit
|
200
168
|
Requires-Dist: streamlit<1.46,>=1.45.0; extra == 'zzz-test-streamlit'
|
201
169
|
Provides-Extra: zzz-test-sys
|
202
|
-
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-sys'
|
203
170
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-sys'
|
204
171
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-sys'
|
205
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-sys'
|
206
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-sys'
|
207
172
|
Provides-Extra: zzz-test-tempfile
|
208
173
|
Provides-Extra: zzz-test-tenacity
|
209
174
|
Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-tenacity'
|
@@ -213,24 +178,18 @@ Provides-Extra: zzz-test-timer
|
|
213
178
|
Provides-Extra: zzz-test-traceback
|
214
179
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-traceback'
|
215
180
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-traceback'
|
216
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-traceback'
|
217
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-traceback'
|
218
181
|
Provides-Extra: zzz-test-types
|
219
182
|
Provides-Extra: zzz-test-typing
|
220
183
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-typing'
|
221
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-typing'
|
222
184
|
Provides-Extra: zzz-test-tzlocal
|
223
|
-
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-tzlocal'
|
224
185
|
Provides-Extra: zzz-test-uuid
|
225
186
|
Provides-Extra: zzz-test-version
|
226
187
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-version'
|
227
188
|
Provides-Extra: zzz-test-warnings
|
228
189
|
Provides-Extra: zzz-test-whenever
|
229
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-whenever'
|
230
190
|
Provides-Extra: zzz-test-zipfile
|
231
191
|
Provides-Extra: zzz-test-zoneinfo
|
232
192
|
Requires-Dist: tzdata<2025.3,>=2025.2; extra == 'zzz-test-zoneinfo'
|
233
|
-
Requires-Dist: whenever<0.9,>=0.8.5; extra == 'zzz-test-zoneinfo'
|
234
193
|
Description-Content-Type: text/markdown
|
235
194
|
|
236
195
|
[](https://badge.fury.io/py/dycw-utilities)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=V2NCsw2lQ18h6ZVmnJk8Ff3KYOijFAMl6rMmt_TUQJk,60
|
2
2
|
utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
|
3
|
-
utilities/altair.py,sha256=
|
3
|
+
utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
|
4
4
|
utilities/asyncio.py,sha256=lvdgBhuMtxq0dpiwF9g2WMMrit3kqXibN1V5NZ4xdbo,38046
|
5
5
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
6
6
|
utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
|
@@ -16,23 +16,23 @@ utilities/datetime.py,sha256=aiPh2OZK2g9gn4yEeSO0lODOmvx8U_rGn6XeSzyk4VY,38738
|
|
16
16
|
utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
|
17
17
|
utilities/errors.py,sha256=nC7ZYtxxDBMfrTHtT_MByBfup_wfGQFRo3eDt-0ZPe8,1045
|
18
18
|
utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
|
19
|
-
utilities/fastapi.py,sha256=
|
20
|
-
utilities/fpdf2.py,sha256=
|
19
|
+
utilities/fastapi.py,sha256=8ABDSOH99j7H8cpKLZhUT2JzU18wiBqcTQuBUlTe-QE,2937
|
20
|
+
utilities/fpdf2.py,sha256=lqizPXpzdwfJk3ChcbbWVa7WNXO2uvy7KDzWQtVTnXA,1831
|
21
21
|
utilities/functions.py,sha256=jgt592voaHNtX56qX0SRvFveVCRmSIxCZmqvpLZCnY8,27305
|
22
22
|
utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
|
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=Wpw_jX9TVAXAI13JuPkYbMYlLod9_o7V51x79hhZabU,47332
|
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
31
|
utilities/iterables.py,sha256=mDqw2_0MUVp-P8FklgcaVTi2TXduH0MxbhTDzzhSBho,44915
|
32
32
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
33
33
|
utilities/libcst.py,sha256=Jto5ppzRzsxn4AD32IS8n0lbgLYXwsVJB6EY8giNZyY,4974
|
34
|
-
utilities/lightweight_charts.py,sha256=
|
35
|
-
utilities/logging.py,sha256=
|
34
|
+
utilities/lightweight_charts.py,sha256=JrkrAZMo6JID2Eoc9QCc05Y_pK4l2zsApIhmii1z2Ig,2764
|
35
|
+
utilities/logging.py,sha256=9XpYHrSVnW2_wYTWQb7LllVxcgq-U2vjUIlM4M60LCE,18407
|
36
36
|
utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
|
37
37
|
utilities/math.py,sha256=-mQgbah-dPJwOEWf3SonrFoVZ2AVxMgpeQ3dfVa-oJA,26764
|
38
38
|
utilities/memory_profiler.py,sha256=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
|
@@ -41,12 +41,12 @@ utilities/more_itertools.py,sha256=tBbjjKx8_Uv_TCjxhPwrGfAx_jRHtvLIZqXVWAsjzqA,8
|
|
41
41
|
utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
|
42
42
|
utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
|
43
43
|
utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
|
44
|
-
utilities/orjson.py,sha256=
|
44
|
+
utilities/orjson.py,sha256=vmPsuOOxrQBAg6aEVVKHfOX9A04QlSa162El5HrIT9E,36889
|
45
45
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
46
46
|
utilities/parse.py,sha256=vsZ2jf_ceSI_Kta9titixufysJaVXh0Whjz1T4awJZw,18938
|
47
47
|
utilities/pathlib.py,sha256=PK41rf1c9Wqv7h8f5R7H3_Lhq_gQZTUJD5tu3gMHVaU,3247
|
48
48
|
utilities/period.py,sha256=o4wXYEXVlFomop4-Ra4L0yRP4i99NZFjIe_fa7NdZck,11024
|
49
|
-
utilities/pickle.py,sha256=
|
49
|
+
utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
|
50
50
|
utilities/platform.py,sha256=48IOKx1IC6ZJXWG-b56ZQptITcNFhWRjELW72o2dGTA,2398
|
51
51
|
utilities/polars.py,sha256=QlmUpYTqHNkcLnWOQh1TW22W2QyLzvifCvBcbsqhpdE,63272
|
52
52
|
utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
|
@@ -54,10 +54,10 @@ utilities/pottery.py,sha256=-YikdIQFqhFXifyMq5R5z6V6N8y639gAnVVXR4jXxdc,3568
|
|
54
54
|
utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
|
55
55
|
utilities/psutil.py,sha256=RtbLKOoIJhqrJmEoHDBVeSD-KPzshtS0FtRXBP9_w2s,3751
|
56
56
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
utilities/pydantic.py,sha256=
|
58
|
-
utilities/pyinstrument.py,sha256=
|
57
|
+
utilities/pydantic.py,sha256=aP6OKowg2Md4rgQuQ5qTSF4bTbBuq7WtRb7zS3JSRGY,1841
|
58
|
+
utilities/pyinstrument.py,sha256=KU7_wPX63TY_8kSps0WZ0ijpN7vXQUkp175vdg-CIdE,899
|
59
59
|
utilities/pyrsistent.py,sha256=wVOVIe_68AAaa-lUE9y-TEzDawVp1uEIc_zfoDgr5ww,2287
|
60
|
-
utilities/pytest.py,sha256=
|
60
|
+
utilities/pytest.py,sha256=zP4CWKXpRVk4aRDRxolUAvqQwX7wgDO8lzmkQfuZaZo,7832
|
61
61
|
utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
|
62
62
|
utilities/python_dotenv.py,sha256=edXsvHZhZnYeqfMfrsRRpj7_9eJI6uizh3xLx8Q9B3w,3228
|
63
63
|
utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
|
@@ -79,18 +79,19 @@ utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
|
|
79
79
|
utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
|
80
80
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
81
81
|
utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
|
82
|
-
utilities/traceback.py,sha256=
|
82
|
+
utilities/traceback.py,sha256=l9onlqDdW5GCSIFbU_-htBE7KlsvsJdNtGrK6-k0RCQ,8759
|
83
83
|
utilities/types.py,sha256=gP04CcCOyFrG7BgblVCsrrChiuO2x842NDVW-GF7odo,18370
|
84
|
-
utilities/typing.py,sha256=
|
84
|
+
utilities/typing.py,sha256=kQWywPcRbFBKmvQBELmgbiqSHsnlo_D0ru53vl6KDeY,13846
|
85
85
|
utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
|
86
|
-
utilities/tzlocal.py,sha256=
|
86
|
+
utilities/tzlocal.py,sha256=P5BjqTiYskeCwjE7i9zycCFXO4MWdZgYCh4jut-LpzA,1042
|
87
87
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
88
88
|
utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
|
89
89
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
90
|
-
utilities/whenever.py,sha256=
|
90
|
+
utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
|
91
|
+
utilities/whenever2.py,sha256=aba0RN9GX_XnRsvmZNbHwkD15NmV4l00VFZ3HTK8vT8,3511
|
91
92
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
92
|
-
utilities/zoneinfo.py,sha256
|
93
|
-
dycw_utilities-0.131.
|
94
|
-
dycw_utilities-0.131.
|
95
|
-
dycw_utilities-0.131.
|
96
|
-
dycw_utilities-0.131.
|
93
|
+
utilities/zoneinfo.py,sha256=tvcgu3QzDxe2suTexi2QzRGpin7VK1TjHa0JYYxT69I,1862
|
94
|
+
dycw_utilities-0.131.3.dist-info/METADATA,sha256=HPiEoWeAQVRNdWO4SDBiTNNhYKhISMiSDIC4fxgJyOo,10222
|
95
|
+
dycw_utilities-0.131.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
96
|
+
dycw_utilities-0.131.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
97
|
+
dycw_utilities-0.131.3.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/altair.py
CHANGED
@@ -23,6 +23,7 @@ from altair import (
|
|
23
23
|
)
|
24
24
|
from altair.utils.schemapi import Undefined
|
25
25
|
|
26
|
+
from utilities.atomicwrites import writer
|
26
27
|
from utilities.functions import ensure_bytes, ensure_number
|
27
28
|
from utilities.iterables import always_iterable
|
28
29
|
from utilities.tempfile import TemporaryDirectory
|
@@ -265,8 +266,6 @@ def save_chart(
|
|
265
266
|
chart: _ChartLike, path: PathLike, /, *, overwrite: bool = False
|
266
267
|
) -> None:
|
267
268
|
"""Atomically save a chart to disk."""
|
268
|
-
from utilities.atomicwrites import writer
|
269
|
-
|
270
269
|
with writer(path, overwrite=overwrite) as temp:
|
271
270
|
chart.save(str(temp), format="png")
|
272
271
|
|
@@ -280,8 +279,6 @@ def save_charts_as_pdf(
|
|
280
279
|
"""Atomically save a chart/set of charts to disk."""
|
281
280
|
from img2pdf import convert
|
282
281
|
|
283
|
-
from utilities.atomicwrites import writer
|
284
|
-
|
285
282
|
with TemporaryDirectory() as temp_dir:
|
286
283
|
temp_paths = [temp_dir.joinpath(f"{i}.png") for i in range(len(charts))]
|
287
284
|
for chart, temp_path in zip(charts, temp_paths, strict=True):
|
utilities/fastapi.py
CHANGED
@@ -9,6 +9,8 @@ from uvicorn import Config, Server
|
|
9
9
|
|
10
10
|
from utilities.asyncio import Looper
|
11
11
|
from utilities.datetime import SECOND, datetime_duration_to_float
|
12
|
+
from utilities.tzlocal import get_now_local # skipif-ci
|
13
|
+
from utilities.whenever import serialize_zoned_datetime # skipif-ci
|
12
14
|
|
13
15
|
if TYPE_CHECKING:
|
14
16
|
from types import TracebackType
|
@@ -29,9 +31,6 @@ class _PingerReceiverApp(FastAPI):
|
|
29
31
|
|
30
32
|
@self.get("/ping") # skipif-ci
|
31
33
|
def ping() -> str:
|
32
|
-
from utilities.tzlocal import get_now_local # skipif-ci
|
33
|
-
from utilities.whenever import serialize_zoned_datetime # skipif-ci
|
34
|
-
|
35
34
|
now = serialize_zoned_datetime(get_now_local()) # skipif-ci
|
36
35
|
return f"pong @ {now}" # skipif-ci
|
37
36
|
|
utilities/fpdf2.py
CHANGED
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, override
|
|
6
6
|
from fpdf import FPDF
|
7
7
|
from fpdf.enums import XPos, YPos
|
8
8
|
|
9
|
+
from utilities.tzlocal import get_now_local
|
10
|
+
|
9
11
|
if TYPE_CHECKING:
|
10
12
|
from collections.abc import Iterator
|
11
13
|
|
@@ -43,8 +45,6 @@ def yield_pdf(*, header: str | None = None) -> Iterator[_BasePDF]:
|
|
43
45
|
|
44
46
|
@override
|
45
47
|
def footer(self) -> None:
|
46
|
-
from utilities.tzlocal import get_now_local
|
47
|
-
|
48
48
|
self.set_y(-15)
|
49
49
|
self.set_font(family="Helvetica", style="I", size=8)
|
50
50
|
page_no, now = self.page_no(), get_now_local()
|
utilities/hypothesis.py
CHANGED
@@ -47,6 +47,7 @@ from hypothesis.strategies import (
|
|
47
47
|
uuids,
|
48
48
|
)
|
49
49
|
from hypothesis.utils.conventions import not_set
|
50
|
+
from whenever import Date, DateDelta, PlainDateTime
|
50
51
|
|
51
52
|
from utilities.datetime import (
|
52
53
|
DATETIME_MAX_NAIVE,
|
@@ -88,7 +89,7 @@ from utilities.platform import IS_WINDOWS
|
|
88
89
|
from utilities.sentinel import Sentinel, sentinel
|
89
90
|
from utilities.tempfile import TEMP_DIR, TemporaryDirectory
|
90
91
|
from utilities.version import Version
|
91
|
-
from utilities.zoneinfo import UTC
|
92
|
+
from utilities.zoneinfo import UTC, ensure_time_zone
|
92
93
|
|
93
94
|
if TYPE_CHECKING:
|
94
95
|
from collections.abc import Collection, Hashable, Iterable, Iterator, Sequence
|
@@ -96,9 +97,10 @@ if TYPE_CHECKING:
|
|
96
97
|
|
97
98
|
from hypothesis.database import ExampleDatabase
|
98
99
|
from numpy.random import RandomState
|
100
|
+
from whenever import ZonedDateTime
|
99
101
|
|
100
102
|
from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
|
101
|
-
from utilities.types import Duration, Number, RoundMode
|
103
|
+
from utilities.types import Duration, Number, RoundMode, TimeZoneLike
|
102
104
|
|
103
105
|
|
104
106
|
_T = TypeVar("_T")
|
@@ -158,6 +160,45 @@ def bool_arrays(
|
|
158
160
|
##
|
159
161
|
|
160
162
|
|
163
|
+
@composite
|
164
|
+
def date_deltas_whenever(
|
165
|
+
draw: DrawFn,
|
166
|
+
/,
|
167
|
+
*,
|
168
|
+
min_value: MaybeSearchStrategy[DateDelta | None] = None,
|
169
|
+
max_value: MaybeSearchStrategy[DateDelta | None] = None,
|
170
|
+
) -> DateDelta:
|
171
|
+
"""Strategy for generating date deltas."""
|
172
|
+
from utilities.whenever2 import DATE_DELTA_MAX, DATE_DELTA_MIN
|
173
|
+
|
174
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
175
|
+
match min_value_:
|
176
|
+
case None:
|
177
|
+
min_value_ = DATE_DELTA_MIN
|
178
|
+
case DateDelta():
|
179
|
+
...
|
180
|
+
case _ as never:
|
181
|
+
assert_never(never)
|
182
|
+
match max_value_:
|
183
|
+
case None:
|
184
|
+
max_value_ = DATE_DELTA_MAX
|
185
|
+
case DateDelta():
|
186
|
+
...
|
187
|
+
case _ as never:
|
188
|
+
assert_never(never)
|
189
|
+
min_years, min_months, min_days = min_value_.in_years_months_days()
|
190
|
+
assert min_years == 0
|
191
|
+
assert min_months == 0
|
192
|
+
max_years, max_months, max_days = max_value_.in_years_months_days()
|
193
|
+
assert max_years == 0
|
194
|
+
assert max_months == 0
|
195
|
+
days = draw(integers(min_value=min_days, max_value=max_days))
|
196
|
+
return DateDelta(days=days)
|
197
|
+
|
198
|
+
|
199
|
+
##
|
200
|
+
|
201
|
+
|
161
202
|
@composite
|
162
203
|
def date_durations(
|
163
204
|
draw: DrawFn,
|
@@ -238,6 +279,41 @@ def dates_two_digit_year(
|
|
238
279
|
##
|
239
280
|
|
240
281
|
|
282
|
+
@composite
|
283
|
+
def dates_whenever(
|
284
|
+
draw: DrawFn,
|
285
|
+
/,
|
286
|
+
*,
|
287
|
+
min_value: MaybeSearchStrategy[Date | None] = None,
|
288
|
+
max_value: MaybeSearchStrategy[Date | None] = None,
|
289
|
+
) -> Date:
|
290
|
+
"""Strategy for generating dates."""
|
291
|
+
from utilities.whenever2 import DATE_MAX, DATE_MIN
|
292
|
+
|
293
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
294
|
+
match min_value_:
|
295
|
+
case None:
|
296
|
+
min_value_ = DATE_MIN
|
297
|
+
case Date():
|
298
|
+
...
|
299
|
+
case _ as never:
|
300
|
+
assert_never(never)
|
301
|
+
match max_value_:
|
302
|
+
case None:
|
303
|
+
max_value_ = DATE_MAX
|
304
|
+
case Date():
|
305
|
+
...
|
306
|
+
case _ as never:
|
307
|
+
assert_never(never)
|
308
|
+
py_date = draw(
|
309
|
+
dates(min_value=min_value_.py_date(), max_value=max_value_.py_date())
|
310
|
+
)
|
311
|
+
return Date.from_py_date(py_date)
|
312
|
+
|
313
|
+
|
314
|
+
##
|
315
|
+
|
316
|
+
|
241
317
|
@composite
|
242
318
|
def datetime_durations(
|
243
319
|
draw: DrawFn,
|
@@ -920,7 +996,7 @@ def _pairs_map(elements: list[_T], /) -> tuple[_T, _T]:
|
|
920
996
|
|
921
997
|
def paths() -> SearchStrategy[Path]:
|
922
998
|
"""Strategy for generating `Path`s."""
|
923
|
-
reserved = {"NUL"}
|
999
|
+
reserved = {"AUX", "NUL"}
|
924
1000
|
strategy = text_ascii(min_size=1, max_size=10).filter(lambda x: x not in reserved)
|
925
1001
|
return lists(strategy, max_size=10).map(lambda parts: Path(*parts))
|
926
1002
|
|
@@ -965,6 +1041,43 @@ class PlainDateTimesError(Exception):
|
|
965
1041
|
##
|
966
1042
|
|
967
1043
|
|
1044
|
+
@composite
|
1045
|
+
def plain_datetimes_whenever(
|
1046
|
+
draw: DrawFn,
|
1047
|
+
/,
|
1048
|
+
*,
|
1049
|
+
min_value: MaybeSearchStrategy[PlainDateTime | None] = None,
|
1050
|
+
max_value: MaybeSearchStrategy[PlainDateTime | None] = None,
|
1051
|
+
) -> PlainDateTime:
|
1052
|
+
"""Strategy for generating plain datetimes."""
|
1053
|
+
from utilities.whenever2 import PLAIN_DATE_TIME_MAX, PLAIN_DATE_TIME_MIN
|
1054
|
+
|
1055
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1056
|
+
match min_value_:
|
1057
|
+
case None:
|
1058
|
+
min_value_ = PLAIN_DATE_TIME_MIN
|
1059
|
+
case PlainDateTime():
|
1060
|
+
...
|
1061
|
+
case _ as never:
|
1062
|
+
assert_never(never)
|
1063
|
+
match max_value_:
|
1064
|
+
case None:
|
1065
|
+
max_value_ = PLAIN_DATE_TIME_MAX
|
1066
|
+
case PlainDateTime():
|
1067
|
+
...
|
1068
|
+
case _ as never:
|
1069
|
+
assert_never(never)
|
1070
|
+
py_datetime = draw(
|
1071
|
+
datetimes(
|
1072
|
+
min_value=min_value_.py_datetime(), max_value=max_value_.py_datetime()
|
1073
|
+
)
|
1074
|
+
)
|
1075
|
+
return PlainDateTime.from_py_datetime(py_datetime)
|
1076
|
+
|
1077
|
+
|
1078
|
+
##
|
1079
|
+
|
1080
|
+
|
968
1081
|
@composite
|
969
1082
|
def random_states(
|
970
1083
|
draw: DrawFn, /, *, seed: MaybeSearchStrategy[int | None] = None
|
@@ -1427,6 +1540,46 @@ class ZonedDateTimesError(Exception):
|
|
1427
1540
|
return "Rounding requires a timedelta; got None"
|
1428
1541
|
|
1429
1542
|
|
1543
|
+
##
|
1544
|
+
|
1545
|
+
|
1546
|
+
@composite
|
1547
|
+
def zoned_datetimes_whenever(
|
1548
|
+
draw: DrawFn,
|
1549
|
+
/,
|
1550
|
+
*,
|
1551
|
+
min_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
|
1552
|
+
max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
|
1553
|
+
time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
|
1554
|
+
) -> ZonedDateTime:
|
1555
|
+
"""Strategy for generating zoned datetimes."""
|
1556
|
+
from whenever import PlainDateTime, ZonedDateTime
|
1557
|
+
|
1558
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1559
|
+
time_zone_ = ensure_time_zone(draw2(draw, time_zone))
|
1560
|
+
match min_value_:
|
1561
|
+
case None | PlainDateTime():
|
1562
|
+
...
|
1563
|
+
case ZonedDateTime():
|
1564
|
+
with assume_does_not_raise(ValueError):
|
1565
|
+
min_value_ = min_value_.to_tz(time_zone_.key).to_plain()
|
1566
|
+
case _ as never:
|
1567
|
+
assert_never(never)
|
1568
|
+
match max_value_:
|
1569
|
+
case None | PlainDateTime():
|
1570
|
+
...
|
1571
|
+
case ZonedDateTime():
|
1572
|
+
with assume_does_not_raise(ValueError):
|
1573
|
+
max_value_ = max_value_.to_tz(time_zone_.key).to_plain()
|
1574
|
+
case _ as never:
|
1575
|
+
assert_never(never)
|
1576
|
+
plain_datetime = draw(
|
1577
|
+
plain_datetimes_whenever(min_value=min_value_, max_value=max_value_)
|
1578
|
+
)
|
1579
|
+
with assume_does_not_raise(ValueError):
|
1580
|
+
return plain_datetime.assume_tz(time_zone_.key, disambiguate="raise")
|
1581
|
+
|
1582
|
+
|
1430
1583
|
__all__ = [
|
1431
1584
|
"Draw2Error",
|
1432
1585
|
"MaybeSearchStrategy",
|
@@ -1435,8 +1588,10 @@ __all__ = [
|
|
1435
1588
|
"ZonedDateTimesError",
|
1436
1589
|
"assume_does_not_raise",
|
1437
1590
|
"bool_arrays",
|
1591
|
+
"date_deltas_whenever",
|
1438
1592
|
"date_durations",
|
1439
1593
|
"dates_two_digit_year",
|
1594
|
+
"dates_whenever",
|
1440
1595
|
"datetime_durations",
|
1441
1596
|
"draw2",
|
1442
1597
|
"float32s",
|
@@ -1460,6 +1615,7 @@ __all__ = [
|
|
1460
1615
|
"paths",
|
1461
1616
|
"plain_datetimes",
|
1462
1617
|
"plain_datetimes",
|
1618
|
+
"plain_datetimes_whenever",
|
1463
1619
|
"random_states",
|
1464
1620
|
"sentinels",
|
1465
1621
|
"sets_fixed_length",
|
@@ -1480,4 +1636,5 @@ __all__ = [
|
|
1480
1636
|
"uint64s",
|
1481
1637
|
"versions",
|
1482
1638
|
"zoned_datetimes",
|
1639
|
+
"zoned_datetimes_whenever",
|
1483
1640
|
]
|
utilities/lightweight_charts.py
CHANGED
@@ -4,6 +4,7 @@ from contextlib import asynccontextmanager
|
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from typing import TYPE_CHECKING, override
|
6
6
|
|
7
|
+
from utilities.atomicwrites import writer # pragma: no cover
|
7
8
|
from utilities.iterables import OneEmptyError, OneNonUniqueError, one
|
8
9
|
from utilities.reprlib import get_repr
|
9
10
|
|
@@ -23,8 +24,6 @@ if TYPE_CHECKING:
|
|
23
24
|
|
24
25
|
def save_chart(chart: Chart, path: PathLike, /, *, overwrite: bool = False) -> None:
|
25
26
|
"""Atomically save a chart to disk."""
|
26
|
-
from utilities.atomicwrites import writer # pragma: no cover
|
27
|
-
|
28
27
|
chart.show(block=False) # pragma: no cover
|
29
28
|
with ( # pragma: no cover
|
30
29
|
writer(path, overwrite=overwrite) as temp,
|
utilities/logging.py
CHANGED
@@ -32,6 +32,7 @@ from typing import (
|
|
32
32
|
override,
|
33
33
|
)
|
34
34
|
|
35
|
+
from utilities.atomicwrites import move_many
|
35
36
|
from utilities.dataclasses import replace_non_sentinel
|
36
37
|
from utilities.datetime import (
|
37
38
|
SECOND,
|
@@ -43,6 +44,7 @@ from utilities.errors import ImpossibleCaseError
|
|
43
44
|
from utilities.iterables import OneEmptyError, always_iterable, one
|
44
45
|
from utilities.pathlib import ensure_suffix, get_path
|
45
46
|
from utilities.sentinel import Sentinel, sentinel
|
47
|
+
from utilities.tzlocal import get_now_local
|
46
48
|
|
47
49
|
if TYPE_CHECKING:
|
48
50
|
from collections.abc import Callable, Iterable, Mapping
|
@@ -190,7 +192,7 @@ def get_formatter(
|
|
190
192
|
) -> Formatter:
|
191
193
|
"""Get the formatter; colored if available."""
|
192
194
|
if whenever:
|
193
|
-
from utilities.
|
195
|
+
from utilities.whenever2 import WheneverLogRecord
|
194
196
|
|
195
197
|
setLogRecordFactory(WheneverLogRecord)
|
196
198
|
format_ = format_.replace("{asctime}", "{zoned_datetime}")
|
@@ -426,8 +428,6 @@ def _compute_rollover_actions(
|
|
426
428
|
patterns: _RolloverPatterns | None = None,
|
427
429
|
backup_count: int = 1,
|
428
430
|
) -> _RolloverActions:
|
429
|
-
from utilities.tzlocal import get_now_local
|
430
|
-
|
431
431
|
patterns = (
|
432
432
|
_compute_rollover_patterns(stem, suffix) if patterns is None else patterns
|
433
433
|
)
|
@@ -470,8 +470,6 @@ class _RolloverActions:
|
|
470
470
|
rotations: set[_Rotation] = field(default_factory=set)
|
471
471
|
|
472
472
|
def do(self) -> None:
|
473
|
-
from utilities.atomicwrites import move_many
|
474
|
-
|
475
473
|
for deletion in self.deletions:
|
476
474
|
deletion.delete()
|
477
475
|
move_many(*((r.file.path, r.destination) for r in self.rotations))
|
utilities/orjson.py
CHANGED
utilities/pickle.py
CHANGED
@@ -4,6 +4,8 @@ import gzip
|
|
4
4
|
from pickle import dump, load
|
5
5
|
from typing import TYPE_CHECKING, Any
|
6
6
|
|
7
|
+
from utilities.atomicwrites import writer
|
8
|
+
|
7
9
|
if TYPE_CHECKING:
|
8
10
|
from utilities.types import PathLike
|
9
11
|
|
@@ -16,8 +18,6 @@ def read_pickle(path: PathLike, /) -> Any:
|
|
16
18
|
|
17
19
|
def write_pickle(obj: Any, path: PathLike, /, *, overwrite: bool = False) -> None:
|
18
20
|
"""Write an object to disk."""
|
19
|
-
from utilities.atomicwrites import writer
|
20
|
-
|
21
21
|
with writer(path, overwrite=overwrite) as temp, gzip.open(temp, mode="wb") as gz:
|
22
22
|
dump(obj, gz)
|
23
23
|
|
utilities/pydantic.py
CHANGED
@@ -6,6 +6,8 @@ from typing import TYPE_CHECKING, TypeVar, override
|
|
6
6
|
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
|
+
from utilities.atomicwrites import writer
|
10
|
+
|
9
11
|
if TYPE_CHECKING:
|
10
12
|
from utilities.types import PathLike
|
11
13
|
|
@@ -52,8 +54,6 @@ class _LoadModelIsADirectoryError(LoadModelError):
|
|
52
54
|
|
53
55
|
|
54
56
|
def save_model(model: BaseModel, path: PathLike, /, *, overwrite: bool = False) -> None:
|
55
|
-
from utilities.atomicwrites import writer
|
56
|
-
|
57
57
|
with writer(path, overwrite=overwrite) as temp, temp.open(mode="w") as fh:
|
58
58
|
_ = fh.write(model.model_dump_json())
|
59
59
|
|
utilities/pyinstrument.py
CHANGED
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING
|
|
6
6
|
|
7
7
|
from pyinstrument.profiler import Profiler
|
8
8
|
|
9
|
+
from utilities.atomicwrites import writer
|
9
10
|
from utilities.datetime import serialize_compact
|
10
11
|
from utilities.pathlib import get_path
|
11
12
|
from utilities.tzlocal import get_now_local
|
@@ -19,8 +20,6 @@ if TYPE_CHECKING:
|
|
19
20
|
@contextmanager
|
20
21
|
def profile(*, path: MaybeCallablePathLike | None = Path.cwd) -> Iterator[None]:
|
21
22
|
"""Profile the contents of a block."""
|
22
|
-
from utilities.atomicwrites import writer
|
23
|
-
|
24
23
|
with Profiler() as profiler:
|
25
24
|
yield
|
26
25
|
filename = get_path(path=path).joinpath(
|
utilities/pytest.py
CHANGED
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, ParamSpec, assert_never, cast, override
|
|
9
9
|
|
10
10
|
from pytest import fixture
|
11
11
|
|
12
|
+
from utilities.atomicwrites import writer
|
12
13
|
from utilities.datetime import datetime_duration_to_float, get_now
|
13
14
|
from utilities.functools import cache
|
14
15
|
from utilities.hashlib import md5_hash
|
@@ -248,8 +249,6 @@ def _throttle_md5_hash(text: str, /) -> str:
|
|
248
249
|
|
249
250
|
|
250
251
|
def _throttle_write(path: Path, now: float, /) -> None:
|
251
|
-
from utilities.atomicwrites import writer
|
252
|
-
|
253
252
|
with writer(path, overwrite=True) as temp, temp.open(mode="w") as fh:
|
254
253
|
_ = fh.write(str(now))
|
255
254
|
|
utilities/traceback.py
CHANGED
@@ -13,6 +13,7 @@ from socket import gethostname
|
|
13
13
|
from traceback import TracebackException
|
14
14
|
from typing import TYPE_CHECKING, override
|
15
15
|
|
16
|
+
from utilities.atomicwrites import writer
|
16
17
|
from utilities.datetime import get_datetime, get_now, serialize_compact
|
17
18
|
from utilities.errors import repr_error
|
18
19
|
from utilities.iterables import OneEmptyError, one
|
@@ -26,8 +27,9 @@ from utilities.reprlib import (
|
|
26
27
|
RICH_MAX_WIDTH,
|
27
28
|
yield_mapping_repr,
|
28
29
|
)
|
30
|
+
from utilities.tzlocal import get_local_time_zone, get_now_local
|
29
31
|
from utilities.version import get_version
|
30
|
-
from utilities.whenever import serialize_duration
|
32
|
+
from utilities.whenever import serialize_duration, serialize_zoned_datetime
|
31
33
|
|
32
34
|
if TYPE_CHECKING:
|
33
35
|
from collections.abc import Callable, Iterator, Sequence
|
@@ -84,9 +86,6 @@ def _yield_header_lines(
|
|
84
86
|
version: MaybeCallableVersionLike | None = None,
|
85
87
|
) -> Iterator[str]:
|
86
88
|
"""Yield the header lines."""
|
87
|
-
from utilities.tzlocal import get_local_time_zone, get_now_local
|
88
|
-
from utilities.whenever import serialize_zoned_datetime
|
89
|
-
|
90
89
|
now = get_now_local()
|
91
90
|
start_use = get_datetime(datetime=start)
|
92
91
|
start_use = (
|
@@ -247,9 +246,6 @@ def _make_except_hook_inner(
|
|
247
246
|
slim = format_exception_stack(exc_val, header=True, start=start, version=version)
|
248
247
|
_ = sys.stderr.write(f"{slim}\n") # don't 'from sys import stderr'
|
249
248
|
if path is not None:
|
250
|
-
from utilities.atomicwrites import writer
|
251
|
-
from utilities.tzlocal import get_now_local
|
252
|
-
|
253
249
|
path = (
|
254
250
|
get_path(path=path)
|
255
251
|
.joinpath(serialize_compact(get_now_local()))
|
utilities/typing.py
CHANGED
@@ -234,7 +234,7 @@ def is_instance_gen(obj: Any, type_: Any, /) -> bool:
|
|
234
234
|
"""Check if an instance relationship holds, except bool<int."""
|
235
235
|
# parent
|
236
236
|
if isinstance(type_, tuple):
|
237
|
-
return any(is_instance_gen(obj, t) for t in type_)
|
237
|
+
return any(is_instance_gen(obj, t) for t in type_) # skipif-ci-and-not-windows
|
238
238
|
if is_literal_type(type_):
|
239
239
|
return obj in get_args(type_)
|
240
240
|
if is_union_type(type_):
|
utilities/tzlocal.py
CHANGED
@@ -20,9 +20,12 @@ def get_local_time_zone() -> ZoneInfo:
|
|
20
20
|
return time_zone
|
21
21
|
|
22
22
|
|
23
|
+
LOCAL_TIME_ZONE = get_local_time_zone()
|
24
|
+
|
25
|
+
|
23
26
|
def get_now_local() -> dt.datetime:
|
24
27
|
"""Get the current local time."""
|
25
|
-
return dt.datetime.now(tz=
|
28
|
+
return dt.datetime.now(tz=LOCAL_TIME_ZONE)
|
26
29
|
|
27
30
|
|
28
31
|
NOW_LOCAL = get_now_local()
|
@@ -37,6 +40,8 @@ TODAY_LOCAL = get_today_local()
|
|
37
40
|
|
38
41
|
|
39
42
|
__all__ = [
|
43
|
+
"LOCAL_TIME_ZONE",
|
44
|
+
"LOCAL_TIME_ZONE",
|
40
45
|
"NOW_LOCAL",
|
41
46
|
"TODAY_LOCAL",
|
42
47
|
"get_local_time_zone",
|
utilities/whenever.py
CHANGED
@@ -4,9 +4,7 @@ import datetime as dt
|
|
4
4
|
import re
|
5
5
|
from contextlib import suppress
|
6
6
|
from dataclasses import dataclass
|
7
|
-
from
|
8
|
-
from logging import LogRecord
|
9
|
-
from typing import TYPE_CHECKING, Any, override
|
7
|
+
from typing import TYPE_CHECKING, override
|
10
8
|
|
11
9
|
from whenever import (
|
12
10
|
Date,
|
@@ -35,8 +33,6 @@ from utilities.re import (
|
|
35
33
|
from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
|
36
34
|
|
37
35
|
if TYPE_CHECKING:
|
38
|
-
from zoneinfo import ZoneInfo
|
39
|
-
|
40
36
|
from utilities.types import (
|
41
37
|
DateLike,
|
42
38
|
DateTimeLike,
|
@@ -565,64 +561,6 @@ class SerializeZonedDateTimeError(Exception):
|
|
565
561
|
##
|
566
562
|
|
567
563
|
|
568
|
-
class WheneverLogRecord(LogRecord):
|
569
|
-
"""Log record powered by `whenever`."""
|
570
|
-
|
571
|
-
zoned_datetime: str
|
572
|
-
|
573
|
-
@override
|
574
|
-
def __init__(
|
575
|
-
self,
|
576
|
-
name: str,
|
577
|
-
level: int,
|
578
|
-
pathname: str,
|
579
|
-
lineno: int,
|
580
|
-
msg: object,
|
581
|
-
args: Any,
|
582
|
-
exc_info: Any,
|
583
|
-
func: str | None = None,
|
584
|
-
sinfo: str | None = None,
|
585
|
-
) -> None:
|
586
|
-
super().__init__(
|
587
|
-
name, level, pathname, lineno, msg, args, exc_info, func, sinfo
|
588
|
-
)
|
589
|
-
length = self._get_length()
|
590
|
-
plain = format(self._get_now().to_plain().format_common_iso(), f"{length}s")
|
591
|
-
time_zone = self._get_time_zone_key()
|
592
|
-
self.zoned_datetime = f"{plain}[{time_zone}]"
|
593
|
-
|
594
|
-
@classmethod
|
595
|
-
@cache
|
596
|
-
def _get_time_zone(cls) -> ZoneInfo:
|
597
|
-
"""Get the local timezone."""
|
598
|
-
try:
|
599
|
-
from utilities.tzlocal import get_local_time_zone
|
600
|
-
except ModuleNotFoundError: # pragma: no cover
|
601
|
-
return UTC
|
602
|
-
return get_local_time_zone()
|
603
|
-
|
604
|
-
@classmethod
|
605
|
-
@cache
|
606
|
-
def _get_time_zone_key(cls) -> str:
|
607
|
-
"""Get the local timezone as a string."""
|
608
|
-
return cls._get_time_zone().key
|
609
|
-
|
610
|
-
@classmethod
|
611
|
-
@cache
|
612
|
-
def _get_length(cls) -> int:
|
613
|
-
"""Get maximum length of a formatted string."""
|
614
|
-
now = cls._get_now().replace(nanosecond=1000).to_plain()
|
615
|
-
return len(now.format_common_iso())
|
616
|
-
|
617
|
-
@classmethod
|
618
|
-
def _get_now(cls) -> ZonedDateTime:
|
619
|
-
"""Get the current zoned datetime."""
|
620
|
-
return ZonedDateTime.now(cls._get_time_zone().key)
|
621
|
-
|
622
|
-
|
623
|
-
##
|
624
|
-
|
625
|
-
|
626
564
|
def _to_datetime_delta(timedelta: dt.timedelta, /) -> DateTimeDelta:
|
627
565
|
"""Serialize a timedelta."""
|
628
566
|
total_microseconds = datetime_duration_to_microseconds(timedelta)
|
@@ -672,7 +610,6 @@ __all__ = [
|
|
672
610
|
"SerializePlainDateTimeError",
|
673
611
|
"SerializeTimeDeltaError",
|
674
612
|
"SerializeZonedDateTimeError",
|
675
|
-
"WheneverLogRecord",
|
676
613
|
"check_valid_zoned_datetime",
|
677
614
|
"ensure_date",
|
678
615
|
"ensure_datetime",
|
utilities/whenever2.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime as dt
|
4
|
+
from functools import cache
|
5
|
+
from logging import LogRecord
|
6
|
+
from typing import TYPE_CHECKING, Any, override
|
7
|
+
|
8
|
+
from whenever import Date, DateTimeDelta, PlainDateTime, ZonedDateTime
|
9
|
+
|
10
|
+
from utilities.tzlocal import LOCAL_TIME_ZONE
|
11
|
+
from utilities.zoneinfo import UTC, get_time_zone_name
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from utilities.types import TimeZoneLike
|
15
|
+
|
16
|
+
|
17
|
+
DATE_MIN = Date.from_py_date(dt.date.min)
|
18
|
+
DATE_MAX = Date.from_py_date(dt.date.max)
|
19
|
+
PLAIN_DATE_TIME_MIN = PlainDateTime.from_py_datetime(dt.datetime.min) # noqa: DTZ901
|
20
|
+
PLAIN_DATE_TIME_MAX = PlainDateTime.from_py_datetime(dt.datetime.max) # noqa: DTZ901
|
21
|
+
ZONED_DATE_TIME_MIN = PLAIN_DATE_TIME_MIN.assume_tz(UTC.key)
|
22
|
+
ZONED_DATE_TIME_MAX = PLAIN_DATE_TIME_MAX.assume_tz(UTC.key)
|
23
|
+
DATE_TIME_DELTA_MIN = DateTimeDelta(days=-3652059, seconds=-316192377600)
|
24
|
+
DATE_TIME_DELTA_MAX = DateTimeDelta(days=3652059, seconds=316192377600)
|
25
|
+
DATE_DELTA_MIN = DATE_TIME_DELTA_MIN.date_part()
|
26
|
+
DATE_DELTA_MAX = DATE_TIME_DELTA_MAX.date_part()
|
27
|
+
TIME_DELTA_MIN = DATE_TIME_DELTA_MIN.time_part()
|
28
|
+
TIME_DELTA_MAX = DATE_TIME_DELTA_MAX.time_part()
|
29
|
+
|
30
|
+
|
31
|
+
##
|
32
|
+
|
33
|
+
|
34
|
+
def from_timestamp(i: float, /, *, time_zone: TimeZoneLike = UTC) -> ZonedDateTime:
|
35
|
+
"""Get a zoned datetime from a timestamp."""
|
36
|
+
return ZonedDateTime.from_timestamp(i, tz=get_time_zone_name(time_zone))
|
37
|
+
|
38
|
+
|
39
|
+
def from_timestamp_millis(i: int, /, *, time_zone: TimeZoneLike = UTC) -> ZonedDateTime:
|
40
|
+
"""Get a zoned datetime from a timestamp (in milliseconds)."""
|
41
|
+
return ZonedDateTime.from_timestamp_millis(i, tz=get_time_zone_name(time_zone))
|
42
|
+
|
43
|
+
|
44
|
+
def from_timestamp_nanos(i: int, /, *, time_zone: TimeZoneLike = UTC) -> ZonedDateTime:
|
45
|
+
"""Get a zoned datetime from a timestamp (in nanoseconds)."""
|
46
|
+
return ZonedDateTime.from_timestamp_nanos(i, tz=get_time_zone_name(time_zone))
|
47
|
+
|
48
|
+
|
49
|
+
##
|
50
|
+
|
51
|
+
|
52
|
+
def get_now(*, time_zone: TimeZoneLike = UTC) -> ZonedDateTime:
|
53
|
+
"""Get the current zoned datetime."""
|
54
|
+
return ZonedDateTime.now(get_time_zone_name(time_zone))
|
55
|
+
|
56
|
+
|
57
|
+
NOW_UTC = get_now(time_zone=UTC)
|
58
|
+
|
59
|
+
|
60
|
+
def get_now_local() -> ZonedDateTime:
|
61
|
+
"""Get the current local time."""
|
62
|
+
return get_now(time_zone="local")
|
63
|
+
|
64
|
+
|
65
|
+
##
|
66
|
+
|
67
|
+
|
68
|
+
class WheneverLogRecord(LogRecord):
|
69
|
+
"""Log record powered by `whenever`."""
|
70
|
+
|
71
|
+
zoned_datetime: str
|
72
|
+
|
73
|
+
@override
|
74
|
+
def __init__(
|
75
|
+
self,
|
76
|
+
name: str,
|
77
|
+
level: int,
|
78
|
+
pathname: str,
|
79
|
+
lineno: int,
|
80
|
+
msg: object,
|
81
|
+
args: Any,
|
82
|
+
exc_info: Any,
|
83
|
+
func: str | None = None,
|
84
|
+
sinfo: str | None = None,
|
85
|
+
) -> None:
|
86
|
+
super().__init__(
|
87
|
+
name, level, pathname, lineno, msg, args, exc_info, func, sinfo
|
88
|
+
)
|
89
|
+
length = self._get_length()
|
90
|
+
plain = format(get_now_local().to_plain().format_common_iso(), f"{length}s")
|
91
|
+
self.zoned_datetime = f"{plain}[{LOCAL_TIME_ZONE.key}]"
|
92
|
+
|
93
|
+
@classmethod
|
94
|
+
@cache
|
95
|
+
def _get_length(cls) -> int:
|
96
|
+
"""Get maximum length of a formatted string."""
|
97
|
+
now = get_now_local().replace(nanosecond=1000).to_plain()
|
98
|
+
return len(now.format_common_iso())
|
99
|
+
|
100
|
+
|
101
|
+
__all__ = [
|
102
|
+
"DATE_DELTA_MAX",
|
103
|
+
"DATE_DELTA_MIN",
|
104
|
+
"DATE_MAX",
|
105
|
+
"DATE_MIN",
|
106
|
+
"DATE_TIME_DELTA_MAX",
|
107
|
+
"DATE_TIME_DELTA_MIN",
|
108
|
+
"PLAIN_DATE_TIME_MAX",
|
109
|
+
"PLAIN_DATE_TIME_MIN",
|
110
|
+
"TIME_DELTA_MAX",
|
111
|
+
"TIME_DELTA_MIN",
|
112
|
+
"ZONED_DATE_TIME_MAX",
|
113
|
+
"ZONED_DATE_TIME_MIN",
|
114
|
+
"WheneverLogRecord",
|
115
|
+
"from_timestamp",
|
116
|
+
"from_timestamp_millis",
|
117
|
+
"from_timestamp_nanos",
|
118
|
+
"get_now",
|
119
|
+
"get_now",
|
120
|
+
"get_now_local",
|
121
|
+
]
|
utilities/zoneinfo.py
CHANGED
@@ -5,6 +5,8 @@ from dataclasses import dataclass
|
|
5
5
|
from typing import TYPE_CHECKING, assert_never, cast, override
|
6
6
|
from zoneinfo import ZoneInfo
|
7
7
|
|
8
|
+
from utilities.tzlocal import get_local_time_zone
|
9
|
+
|
8
10
|
if TYPE_CHECKING:
|
9
11
|
from utilities.types import TimeZone, TimeZoneLike
|
10
12
|
|
@@ -21,8 +23,6 @@ def ensure_time_zone(obj: TimeZoneLike, /) -> ZoneInfo:
|
|
21
23
|
case ZoneInfo() as zone_info:
|
22
24
|
return zone_info
|
23
25
|
case "local":
|
24
|
-
from utilities.tzlocal import get_local_time_zone
|
25
|
-
|
26
26
|
return get_local_time_zone()
|
27
27
|
case str() as key:
|
28
28
|
return ZoneInfo(key)
|
File without changes
|
File without changes
|