dycw-utilities 0.129.10__py3-none-any.whl → 0.175.17__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.175.17.dist-info/METADATA +34 -0
- dycw_utilities-0.175.17.dist-info/RECORD +103 -0
- dycw_utilities-0.175.17.dist-info/WHEEL +4 -0
- dycw_utilities-0.175.17.dist-info/entry_points.txt +4 -0
- utilities/__init__.py +1 -1
- utilities/altair.py +14 -14
- utilities/asyncio.py +350 -819
- utilities/atomicwrites.py +18 -6
- utilities/atools.py +77 -22
- utilities/cachetools.py +24 -29
- utilities/click.py +393 -237
- utilities/concurrent.py +8 -11
- utilities/contextlib.py +216 -17
- utilities/contextvars.py +20 -1
- utilities/cryptography.py +3 -3
- utilities/dataclasses.py +83 -118
- utilities/docker.py +293 -0
- utilities/enum.py +26 -23
- utilities/errors.py +17 -3
- utilities/fastapi.py +29 -65
- utilities/fpdf2.py +3 -3
- utilities/functions.py +169 -416
- utilities/functools.py +18 -19
- utilities/git.py +9 -30
- utilities/grp.py +28 -0
- utilities/gzip.py +31 -0
- utilities/http.py +3 -2
- utilities/hypothesis.py +738 -589
- utilities/importlib.py +17 -1
- utilities/inflect.py +25 -0
- utilities/iterables.py +194 -262
- utilities/jinja2.py +148 -0
- utilities/json.py +70 -0
- utilities/libcst.py +38 -17
- utilities/lightweight_charts.py +5 -9
- utilities/logging.py +345 -543
- utilities/math.py +18 -13
- utilities/memory_profiler.py +11 -15
- utilities/more_itertools.py +200 -131
- utilities/operator.py +33 -29
- utilities/optuna.py +6 -6
- utilities/orjson.py +272 -137
- utilities/os.py +61 -4
- utilities/parse.py +59 -61
- utilities/pathlib.py +281 -40
- utilities/permissions.py +298 -0
- utilities/pickle.py +2 -2
- utilities/platform.py +24 -5
- utilities/polars.py +1214 -430
- utilities/polars_ols.py +1 -1
- utilities/postgres.py +408 -0
- utilities/pottery.py +113 -26
- utilities/pqdm.py +10 -11
- utilities/psutil.py +6 -57
- utilities/pwd.py +28 -0
- utilities/pydantic.py +4 -54
- utilities/pydantic_settings.py +240 -0
- utilities/pydantic_settings_sops.py +76 -0
- utilities/pyinstrument.py +8 -10
- utilities/pytest.py +227 -121
- utilities/pytest_plugins/__init__.py +1 -0
- utilities/pytest_plugins/pytest_randomly.py +23 -0
- utilities/pytest_plugins/pytest_regressions.py +56 -0
- utilities/pytest_regressions.py +26 -46
- utilities/random.py +13 -9
- utilities/re.py +58 -28
- utilities/redis.py +401 -550
- utilities/scipy.py +1 -1
- utilities/sentinel.py +10 -0
- utilities/shelve.py +4 -1
- utilities/shutil.py +25 -0
- utilities/slack_sdk.py +36 -106
- utilities/sqlalchemy.py +502 -473
- utilities/sqlalchemy_polars.py +38 -94
- utilities/string.py +2 -3
- utilities/subprocess.py +1572 -0
- utilities/tempfile.py +86 -4
- utilities/testbook.py +50 -0
- utilities/text.py +165 -42
- utilities/timer.py +37 -65
- utilities/traceback.py +158 -929
- utilities/types.py +146 -116
- utilities/typing.py +531 -71
- utilities/tzdata.py +1 -53
- utilities/tzlocal.py +6 -23
- utilities/uuid.py +43 -5
- utilities/version.py +27 -26
- utilities/whenever.py +1776 -386
- utilities/zoneinfo.py +84 -22
- dycw_utilities-0.129.10.dist-info/METADATA +0 -241
- dycw_utilities-0.129.10.dist-info/RECORD +0 -96
- dycw_utilities-0.129.10.dist-info/WHEEL +0 -4
- dycw_utilities-0.129.10.dist-info/licenses/LICENSE +0 -21
- utilities/datetime.py +0 -1409
- utilities/eventkit.py +0 -402
- utilities/loguru.py +0 -144
- utilities/luigi.py +0 -228
- utilities/period.py +0 -324
- utilities/pyrsistent.py +0 -89
- utilities/python_dotenv.py +0 -105
- utilities/streamlit.py +0 -105
- utilities/sys.py +0 -87
- utilities/tenacity.py +0 -145
utilities/concurrent.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
|
|
4
4
|
from functools import partial
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, assert_never
|
|
6
6
|
|
|
7
7
|
from utilities.iterables import apply_to_tuple
|
|
8
8
|
from utilities.os import get_cpu_use
|
|
@@ -15,11 +15,8 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from utilities.os import IntOrAll
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def concurrent_map(
|
|
22
|
-
func: Callable[..., _T],
|
|
18
|
+
def concurrent_map[T](
|
|
19
|
+
func: Callable[..., T],
|
|
23
20
|
/,
|
|
24
21
|
*iterables: Iterable[Any],
|
|
25
22
|
parallelism: Parallelism = "processes",
|
|
@@ -31,7 +28,7 @@ def concurrent_map(
|
|
|
31
28
|
thread_name_prefix: str = "",
|
|
32
29
|
timeout: float | None = None,
|
|
33
30
|
chunksize: int = 1,
|
|
34
|
-
) -> list[
|
|
31
|
+
) -> list[T]:
|
|
35
32
|
"""Concurrent map."""
|
|
36
33
|
return concurrent_starmap(
|
|
37
34
|
func,
|
|
@@ -51,8 +48,8 @@ def concurrent_map(
|
|
|
51
48
|
##
|
|
52
49
|
|
|
53
50
|
|
|
54
|
-
def concurrent_starmap(
|
|
55
|
-
func: Callable[...,
|
|
51
|
+
def concurrent_starmap[T](
|
|
52
|
+
func: Callable[..., T],
|
|
56
53
|
iterable: Iterable[tuple[Any, ...]],
|
|
57
54
|
/,
|
|
58
55
|
*,
|
|
@@ -65,7 +62,7 @@ def concurrent_starmap(
|
|
|
65
62
|
thread_name_prefix: str = "",
|
|
66
63
|
timeout: float | None = None,
|
|
67
64
|
chunksize: int = 1,
|
|
68
|
-
) -> list[
|
|
65
|
+
) -> list[T]:
|
|
69
66
|
"""Concurrent map."""
|
|
70
67
|
max_workers_use = get_cpu_use(n=max_workers)
|
|
71
68
|
apply = partial(apply_to_tuple, func)
|
|
@@ -87,7 +84,7 @@ def concurrent_starmap(
|
|
|
87
84
|
initargs=initargs,
|
|
88
85
|
) as pool:
|
|
89
86
|
result = pool.map(apply, iterable, timeout=timeout, chunksize=chunksize)
|
|
90
|
-
case
|
|
87
|
+
case never:
|
|
91
88
|
assert_never(never)
|
|
92
89
|
return list(result)
|
|
93
90
|
|
utilities/contextlib.py
CHANGED
|
@@ -1,28 +1,223 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
-
from
|
|
5
|
-
from
|
|
4
|
+
from asyncio import create_task, get_event_loop
|
|
5
|
+
from contextlib import (
|
|
6
|
+
_AsyncGeneratorContextManager,
|
|
7
|
+
_GeneratorContextManager,
|
|
8
|
+
asynccontextmanager,
|
|
9
|
+
contextmanager,
|
|
10
|
+
)
|
|
11
|
+
from functools import partial, wraps
|
|
12
|
+
from signal import SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, getsignal, signal
|
|
13
|
+
from typing import TYPE_CHECKING, Any, assert_never, cast, overload
|
|
6
14
|
|
|
7
15
|
if TYPE_CHECKING:
|
|
8
|
-
from collections.abc import Iterator
|
|
9
|
-
from
|
|
16
|
+
from collections.abc import AsyncIterator, Callable, Iterator
|
|
17
|
+
from signal import _HANDLER, _SIGNUM
|
|
18
|
+
from types import FrameType
|
|
10
19
|
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
@overload
|
|
22
|
+
def enhanced_context_manager[**P, T_co](
|
|
23
|
+
func: Callable[P, Iterator[T_co]],
|
|
24
|
+
/,
|
|
25
|
+
*,
|
|
26
|
+
sigabrt: bool = True,
|
|
27
|
+
sigfpe: bool = True,
|
|
28
|
+
sigill: bool = True,
|
|
29
|
+
sigint: bool = True,
|
|
30
|
+
sigsegv: bool = True,
|
|
31
|
+
sigterm: bool = True,
|
|
32
|
+
) -> Callable[P, _GeneratorContextManager[T_co]]: ...
|
|
33
|
+
@overload
|
|
34
|
+
def enhanced_context_manager[**P, T_co](
|
|
35
|
+
func: None = None,
|
|
36
|
+
/,
|
|
37
|
+
*,
|
|
38
|
+
sigabrt: bool = True,
|
|
39
|
+
sigfpe: bool = True,
|
|
40
|
+
sigill: bool = True,
|
|
41
|
+
sigint: bool = True,
|
|
42
|
+
sigsegv: bool = True,
|
|
43
|
+
sigterm: bool = True,
|
|
44
|
+
) -> Callable[
|
|
45
|
+
[Callable[P, Iterator[T_co]]], Callable[P, _GeneratorContextManager[T_co]]
|
|
46
|
+
]: ...
|
|
47
|
+
def enhanced_context_manager[**P, T_co](
|
|
48
|
+
func: Callable[P, Iterator[T_co]] | None = None,
|
|
49
|
+
/,
|
|
50
|
+
*,
|
|
51
|
+
sigabrt: bool = True,
|
|
52
|
+
sigfpe: bool = True,
|
|
53
|
+
sigill: bool = True,
|
|
54
|
+
sigint: bool = True,
|
|
55
|
+
sigsegv: bool = True,
|
|
56
|
+
sigterm: bool = True,
|
|
57
|
+
) -> (
|
|
58
|
+
Callable[P, _GeneratorContextManager[T_co]]
|
|
59
|
+
| Callable[
|
|
60
|
+
[Callable[P, Iterator[T_co]]], Callable[P, _GeneratorContextManager[T_co]]
|
|
61
|
+
]
|
|
62
|
+
):
|
|
63
|
+
if func is None:
|
|
64
|
+
result = partial(
|
|
65
|
+
enhanced_context_manager,
|
|
66
|
+
sigabrt=sigabrt,
|
|
67
|
+
sigfpe=sigfpe,
|
|
68
|
+
sigill=sigill,
|
|
69
|
+
sigint=sigint,
|
|
70
|
+
sigsegv=sigsegv,
|
|
71
|
+
sigterm=sigterm,
|
|
72
|
+
)
|
|
73
|
+
return cast(
|
|
74
|
+
"Callable[[Callable[P, Iterator[T_co]]], Callable[P, _GeneratorContextManager[T_co]]]",
|
|
75
|
+
result,
|
|
76
|
+
)
|
|
77
|
+
make_gcm = contextmanager(func)
|
|
14
78
|
|
|
15
|
-
|
|
16
|
-
|
|
79
|
+
@contextmanager
|
|
80
|
+
@wraps(func)
|
|
81
|
+
def wrapped(*args: P.args, **kwargs: P.kwargs) -> Iterator[T_co]:
|
|
82
|
+
gcm = make_gcm(*args, **kwargs)
|
|
83
|
+
sigabrt0 = _swap_handler(SIGABRT, gcm) if sigabrt else None
|
|
84
|
+
sigfpe0 = _swap_handler(SIGFPE, gcm) if sigfpe else None
|
|
85
|
+
sigill0 = _swap_handler(SIGILL, gcm) if sigill else None
|
|
86
|
+
sigint0 = _swap_handler(SIGINT, gcm) if sigint else None
|
|
87
|
+
sigsegv0 = _swap_handler(SIGSEGV, gcm) if sigsegv else None
|
|
88
|
+
sigterm0 = _swap_handler(SIGTERM, gcm) if sigterm else None
|
|
89
|
+
try:
|
|
90
|
+
with gcm as value:
|
|
91
|
+
yield value
|
|
92
|
+
finally:
|
|
93
|
+
_ = signal(SIGABRT, sigabrt0) if sigabrt else None
|
|
94
|
+
_ = signal(SIGFPE, sigfpe0) if sigfpe else None
|
|
95
|
+
_ = signal(SIGILL, sigill0) if sigill else None
|
|
96
|
+
_ = signal(SIGINT, sigint0) if sigint else None
|
|
97
|
+
_ = signal(SIGSEGV, sigsegv0) if sigsegv else None
|
|
98
|
+
_ = signal(SIGTERM, sigterm0) if sigterm else None
|
|
17
99
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
100
|
+
return wrapped
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@overload
|
|
104
|
+
def enhanced_async_context_manager[**P, T_co](
|
|
105
|
+
func: Callable[P, AsyncIterator[T_co]],
|
|
106
|
+
/,
|
|
107
|
+
*,
|
|
108
|
+
sigabrt: bool = True,
|
|
109
|
+
sigfpe: bool = True,
|
|
110
|
+
sigill: bool = True,
|
|
111
|
+
sigint: bool = True,
|
|
112
|
+
sigsegv: bool = True,
|
|
113
|
+
sigterm: bool = True,
|
|
114
|
+
) -> Callable[P, _AsyncGeneratorContextManager[T_co]]: ...
|
|
115
|
+
@overload
|
|
116
|
+
def enhanced_async_context_manager[**P, T_co](
|
|
117
|
+
func: None = None,
|
|
118
|
+
/,
|
|
119
|
+
*,
|
|
120
|
+
sigabrt: bool = True,
|
|
121
|
+
sigfpe: bool = True,
|
|
122
|
+
sigill: bool = True,
|
|
123
|
+
sigint: bool = True,
|
|
124
|
+
sigsegv: bool = True,
|
|
125
|
+
sigterm: bool = True,
|
|
126
|
+
) -> Callable[
|
|
127
|
+
[Callable[P, AsyncIterator[T_co]]], Callable[P, _AsyncGeneratorContextManager[T_co]]
|
|
128
|
+
]: ...
|
|
129
|
+
def enhanced_async_context_manager[**P, T_co](
|
|
130
|
+
func: Callable[P, AsyncIterator[T_co]] | None = None,
|
|
131
|
+
/,
|
|
132
|
+
*,
|
|
133
|
+
sigabrt: bool = True,
|
|
134
|
+
sigfpe: bool = True,
|
|
135
|
+
sigill: bool = True,
|
|
136
|
+
sigint: bool = True,
|
|
137
|
+
sigsegv: bool = True,
|
|
138
|
+
sigterm: bool = True,
|
|
139
|
+
) -> (
|
|
140
|
+
Callable[P, _AsyncGeneratorContextManager[T_co]]
|
|
141
|
+
| Callable[
|
|
142
|
+
[Callable[P, AsyncIterator[T_co]]],
|
|
143
|
+
Callable[P, _AsyncGeneratorContextManager[T_co]],
|
|
144
|
+
]
|
|
145
|
+
):
|
|
146
|
+
if func is None:
|
|
147
|
+
result = partial(
|
|
148
|
+
enhanced_async_context_manager,
|
|
149
|
+
sigabrt=sigabrt,
|
|
150
|
+
sigfpe=sigfpe,
|
|
151
|
+
sigill=sigill,
|
|
152
|
+
sigint=sigint,
|
|
153
|
+
sigsegv=sigsegv,
|
|
154
|
+
sigterm=sigterm,
|
|
155
|
+
)
|
|
156
|
+
return cast(
|
|
157
|
+
"Callable[[Callable[P, AsyncIterator[T_co]]], Callable[P, _AsyncGeneratorContextManager[T_co]]]",
|
|
158
|
+
result,
|
|
159
|
+
)
|
|
160
|
+
make_agcm = asynccontextmanager(func)
|
|
161
|
+
|
|
162
|
+
@asynccontextmanager
|
|
163
|
+
@wraps(func)
|
|
164
|
+
async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncIterator[T_co]:
|
|
165
|
+
agcm = make_agcm(*args, **kwargs)
|
|
166
|
+
sigabrt0 = _swap_handler(SIGABRT, agcm) if sigabrt else None
|
|
167
|
+
sigfpe0 = _swap_handler(SIGFPE, agcm) if sigfpe else None
|
|
168
|
+
sigill0 = _swap_handler(SIGILL, agcm) if sigill else None
|
|
169
|
+
sigint0 = _swap_handler(SIGINT, agcm) if sigint else None
|
|
170
|
+
sigsegv0 = _swap_handler(SIGSEGV, agcm) if sigsegv else None
|
|
171
|
+
sigterm0 = _swap_handler(SIGTERM, agcm) if sigterm else None
|
|
172
|
+
try:
|
|
173
|
+
async with agcm as value:
|
|
174
|
+
yield value
|
|
175
|
+
finally:
|
|
176
|
+
_ = signal(SIGABRT, sigabrt0) if sigabrt else None
|
|
177
|
+
_ = signal(SIGFPE, sigfpe0) if sigfpe else None
|
|
178
|
+
_ = signal(SIGILL, sigill0) if sigill else None
|
|
179
|
+
_ = signal(SIGINT, sigint0) if sigint else None
|
|
180
|
+
_ = signal(SIGSEGV, sigsegv0) if sigsegv else None
|
|
181
|
+
_ = signal(SIGTERM, sigterm0) if sigterm else None
|
|
182
|
+
|
|
183
|
+
return wrapped
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _swap_handler(
|
|
187
|
+
signum: _SIGNUM,
|
|
188
|
+
obj: _GeneratorContextManager[Any, None, None]
|
|
189
|
+
| _AsyncGeneratorContextManager[Any, None],
|
|
190
|
+
/,
|
|
191
|
+
) -> _HANDLER:
|
|
192
|
+
orig_handler = getsignal(signum)
|
|
193
|
+
new_handler = _make_handler(signum, obj)
|
|
194
|
+
_ = signal(signum, new_handler)
|
|
195
|
+
return orig_handler
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _make_handler(
|
|
199
|
+
signum: _SIGNUM,
|
|
200
|
+
obj: _GeneratorContextManager[Any, None, None]
|
|
201
|
+
| _AsyncGeneratorContextManager[Any, None],
|
|
202
|
+
/,
|
|
203
|
+
) -> Callable[[int, FrameType | None], None]:
|
|
204
|
+
orig_handler = getsignal(signum)
|
|
205
|
+
|
|
206
|
+
def new_handler(signum: int, frame: FrameType | None) -> None:
|
|
207
|
+
match obj: # pragma: no cover
|
|
208
|
+
case _GeneratorContextManager() as gcm:
|
|
209
|
+
_ = gcm.__exit__(None, None, None)
|
|
210
|
+
case _AsyncGeneratorContextManager() as agcm:
|
|
211
|
+
loop = get_event_loop()
|
|
212
|
+
_ = loop.call_soon_threadsafe(
|
|
213
|
+
create_task, agcm.__aexit__(None, None, None)
|
|
214
|
+
)
|
|
215
|
+
case never:
|
|
216
|
+
assert_never(never)
|
|
217
|
+
if callable(orig_handler): # pragma: no cover
|
|
218
|
+
orig_handler(signum, frame)
|
|
219
|
+
|
|
220
|
+
return new_handler
|
|
26
221
|
|
|
27
222
|
|
|
28
223
|
##
|
|
@@ -41,4 +236,8 @@ def suppress_super_object_attribute_error() -> Iterator[None]:
|
|
|
41
236
|
raise
|
|
42
237
|
|
|
43
238
|
|
|
44
|
-
__all__ = [
|
|
239
|
+
__all__ = [
|
|
240
|
+
"enhanced_async_context_manager",
|
|
241
|
+
"enhanced_context_manager",
|
|
242
|
+
"suppress_super_object_attribute_error",
|
|
243
|
+
]
|
utilities/contextvars.py
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from contextlib import contextmanager
|
|
3
4
|
from contextvars import ContextVar
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
|
|
4
10
|
|
|
5
11
|
##
|
|
6
12
|
|
|
@@ -19,4 +25,17 @@ def set_global_breakpoint() -> None:
|
|
|
19
25
|
_ = _GLOBAL_BREAKPOINT.set(True)
|
|
20
26
|
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
##
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@contextmanager
|
|
32
|
+
def yield_set_context(var: ContextVar[bool], /) -> Iterator[None]:
|
|
33
|
+
"""Yield a context var as being set."""
|
|
34
|
+
token = var.set(True)
|
|
35
|
+
try:
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
_ = var.reset(token)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = ["global_breakpoint", "set_global_breakpoint", "yield_set_context"]
|
utilities/cryptography.py
CHANGED
|
@@ -11,18 +11,18 @@ _ENV_VAR = "FERNET_KEY"
|
|
|
11
11
|
|
|
12
12
|
def encrypt(text: str, /, *, env_var: str = _ENV_VAR) -> bytes:
|
|
13
13
|
"""Encrypt a string."""
|
|
14
|
-
return get_fernet(env_var
|
|
14
|
+
return get_fernet(env_var).encrypt(text.encode())
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def decrypt(text: bytes, /, *, env_var: str = _ENV_VAR) -> str:
|
|
18
18
|
"""Encrypt a string."""
|
|
19
|
-
return get_fernet(env_var
|
|
19
|
+
return get_fernet(env_var).decrypt(text).decode()
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
##
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def get_fernet(
|
|
25
|
+
def get_fernet(env_var: str = _ENV_VAR, /) -> Fernet:
|
|
26
26
|
"""Get the Fernet key."""
|
|
27
27
|
if (key := getenv(env_var)) is None:
|
|
28
28
|
raise GetFernetError(env_var=env_var)
|