dycw-utilities 0.133.6__py3-none-any.whl → 0.134.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/RECORD +39 -39
- utilities/__init__.py +1 -1
- utilities/arq.py +13 -20
- utilities/asyncio.py +59 -74
- utilities/atools.py +10 -13
- utilities/cachetools.py +11 -17
- utilities/click.py +61 -55
- utilities/concurrent.py +7 -10
- utilities/dataclasses.py +69 -91
- utilities/enum.py +24 -21
- utilities/eventkit.py +34 -48
- utilities/functions.py +133 -168
- utilities/functools.py +14 -18
- utilities/hypothesis.py +34 -44
- utilities/iterables.py +165 -179
- utilities/luigi.py +3 -15
- utilities/memory_profiler.py +11 -15
- utilities/more_itertools.py +85 -94
- utilities/operator.py +5 -7
- utilities/optuna.py +6 -6
- utilities/pathlib.py +1 -0
- utilities/period.py +7 -9
- utilities/polars.py +5 -16
- utilities/pqdm.py +7 -8
- utilities/pydantic.py +2 -4
- utilities/pytest.py +14 -23
- utilities/python_dotenv.py +5 -9
- utilities/random.py +2 -3
- utilities/redis.py +163 -181
- utilities/slack_sdk.py +2 -2
- utilities/sqlalchemy.py +4 -14
- utilities/timer.py +6 -0
- utilities/typed_settings.py +7 -10
- utilities/types.py +10 -94
- utilities/typing.py +32 -43
- utilities/uuid.py +1 -0
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.133.6.dist-info → dycw_utilities-0.134.0.dist-info}/licenses/LICENSE +0 -0
utilities/pydantic.py
CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import TYPE_CHECKING,
|
5
|
+
from typing import TYPE_CHECKING, override
|
6
6
|
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
@@ -11,8 +11,6 @@ from utilities.atomicwrites import writer
|
|
11
11
|
if TYPE_CHECKING:
|
12
12
|
from utilities.types import PathLike
|
13
13
|
|
14
|
-
_TBaseModel = TypeVar("_TBaseModel", bound=BaseModel)
|
15
|
-
|
16
14
|
|
17
15
|
class HashableBaseModel(BaseModel):
|
18
16
|
"""Subclass of BaseModel which is hashable."""
|
@@ -22,7 +20,7 @@ class HashableBaseModel(BaseModel):
|
|
22
20
|
return hash((type(self), *self.__dict__.values()))
|
23
21
|
|
24
22
|
|
25
|
-
def load_model(model: type[
|
23
|
+
def load_model[T: BaseModel](model: type[T], path: PathLike, /) -> T:
|
26
24
|
path = Path(path)
|
27
25
|
try:
|
28
26
|
with path.open() as fh:
|
utilities/pytest.py
CHANGED
@@ -5,7 +5,7 @@ from functools import partial, wraps
|
|
5
5
|
from inspect import iscoroutinefunction
|
6
6
|
from os import environ
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import TYPE_CHECKING, Any,
|
8
|
+
from typing import TYPE_CHECKING, Any, assert_never, cast, override
|
9
9
|
|
10
10
|
from pytest import fixture
|
11
11
|
from whenever import ZonedDateTime
|
@@ -24,6 +24,7 @@ from utilities.platform import (
|
|
24
24
|
IS_WINDOWS,
|
25
25
|
)
|
26
26
|
from utilities.random import get_state
|
27
|
+
from utilities.types import MaybeCoro
|
27
28
|
from utilities.whenever import SECOND, get_now_local
|
28
29
|
|
29
30
|
if TYPE_CHECKING:
|
@@ -32,7 +33,7 @@ if TYPE_CHECKING:
|
|
32
33
|
|
33
34
|
from whenever import TimeDelta
|
34
35
|
|
35
|
-
from utilities.types import
|
36
|
+
from utilities.types import Coro, PathLike
|
36
37
|
|
37
38
|
try: # WARNING: this package cannot use unguarded `pytest` imports
|
38
39
|
from _pytest.config import Config
|
@@ -56,12 +57,6 @@ else:
|
|
56
57
|
skipif_not_linux = mark.skipif(IS_NOT_LINUX, reason="Skipped for non-Linux")
|
57
58
|
|
58
59
|
|
59
|
-
_P = ParamSpec("_P")
|
60
|
-
|
61
|
-
|
62
|
-
##
|
63
|
-
|
64
|
-
|
65
60
|
def add_pytest_addoption(parser: Parser, options: Sequence[str], /) -> None:
|
66
61
|
"""Add the `--slow`, etc options to pytest.
|
67
62
|
|
@@ -167,27 +162,27 @@ def random_state(*, seed: int) -> Random:
|
|
167
162
|
##
|
168
163
|
|
169
164
|
|
170
|
-
def throttle(
|
165
|
+
def throttle[F: Callable[..., MaybeCoro[None]]](
|
171
166
|
*, root: PathLike | None = None, delta: TimeDelta = SECOND, on_try: bool = False
|
172
|
-
) -> Callable[[
|
167
|
+
) -> Callable[[F], F]:
|
173
168
|
"""Throttle a test. On success by default, on try otherwise."""
|
174
169
|
return cast("Any", partial(_throttle_inner, root=root, delta=delta, on_try=on_try))
|
175
170
|
|
176
171
|
|
177
|
-
def _throttle_inner(
|
178
|
-
func:
|
172
|
+
def _throttle_inner[F: Callable[..., MaybeCoro[None]]](
|
173
|
+
func: F,
|
179
174
|
/,
|
180
175
|
*,
|
181
176
|
root: PathLike | None = None,
|
182
177
|
delta: TimeDelta = SECOND,
|
183
178
|
on_try: bool = False,
|
184
|
-
) ->
|
179
|
+
) -> F:
|
185
180
|
"""Throttle a test function/method."""
|
186
181
|
match bool(iscoroutinefunction(func)), on_try:
|
187
182
|
case False, False:
|
188
183
|
|
189
184
|
@wraps(func)
|
190
|
-
def throttle_sync_on_pass(*args:
|
185
|
+
def throttle_sync_on_pass(*args: Any, **kwargs: Any) -> None:
|
191
186
|
_skipif_recent(root=root, delta=delta)
|
192
187
|
cast("Callable[..., None]", func)(*args, **kwargs)
|
193
188
|
_write(root=root)
|
@@ -197,7 +192,7 @@ def _throttle_inner(
|
|
197
192
|
case False, True:
|
198
193
|
|
199
194
|
@wraps(func)
|
200
|
-
def throttle_sync_on_try(*args:
|
195
|
+
def throttle_sync_on_try(*args: Any, **kwargs: Any) -> None:
|
201
196
|
_skipif_recent(root=root, delta=delta)
|
202
197
|
_write(root=root)
|
203
198
|
cast("Callable[..., None]", func)(*args, **kwargs)
|
@@ -207,11 +202,9 @@ def _throttle_inner(
|
|
207
202
|
case True, False:
|
208
203
|
|
209
204
|
@wraps(func)
|
210
|
-
async def throttle_async_on_pass(
|
211
|
-
*args: _P.args, **kwargs: _P.kwargs
|
212
|
-
) -> None:
|
205
|
+
async def throttle_async_on_pass(*args: Any, **kwargs: Any) -> None:
|
213
206
|
_skipif_recent(root=root, delta=delta)
|
214
|
-
await cast("Callable[...,
|
207
|
+
await cast("Callable[..., Coro[None]]", func)(*args, **kwargs)
|
215
208
|
_write(root=root)
|
216
209
|
|
217
210
|
return cast("Any", throttle_async_on_pass)
|
@@ -219,12 +212,10 @@ def _throttle_inner(
|
|
219
212
|
case True, True:
|
220
213
|
|
221
214
|
@wraps(func)
|
222
|
-
async def throttle_async_on_try(
|
223
|
-
*args: _P.args, **kwargs: _P.kwargs
|
224
|
-
) -> None:
|
215
|
+
async def throttle_async_on_try(*args: Any, **kwargs: Any) -> None:
|
225
216
|
_skipif_recent(root=root, delta=delta)
|
226
217
|
_write(root=root)
|
227
|
-
await cast("Callable[...,
|
218
|
+
await cast("Callable[..., Coro[None]]", func)(*args, **kwargs)
|
228
219
|
|
229
220
|
return cast("Any", throttle_async_on_try)
|
230
221
|
|
utilities/python_dotenv.py
CHANGED
@@ -11,21 +11,17 @@ from utilities.dataclasses import _ParseDataClassMissingValuesError, parse_datac
|
|
11
11
|
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
12
12
|
from utilities.pathlib import get_root
|
13
13
|
from utilities.reprlib import get_repr
|
14
|
+
from utilities.types import Dataclass
|
14
15
|
|
15
16
|
if TYPE_CHECKING:
|
16
17
|
from collections.abc import Mapping
|
17
18
|
from collections.abc import Set as AbstractSet
|
18
19
|
|
19
|
-
from utilities.types import
|
20
|
-
MaybeCallablePathLike,
|
21
|
-
ParseObjectExtra,
|
22
|
-
StrMapping,
|
23
|
-
TDataclass,
|
24
|
-
)
|
20
|
+
from utilities.types import MaybeCallablePathLike, ParseObjectExtra, StrMapping
|
25
21
|
|
26
22
|
|
27
|
-
def load_settings(
|
28
|
-
cls: type[
|
23
|
+
def load_settings[T: Dataclass](
|
24
|
+
cls: type[T],
|
29
25
|
/,
|
30
26
|
*,
|
31
27
|
path: MaybeCallablePathLike | None = Path.cwd,
|
@@ -35,7 +31,7 @@ def load_settings(
|
|
35
31
|
head: bool = False,
|
36
32
|
case_sensitive: bool = False,
|
37
33
|
extra_parsers: ParseObjectExtra | None = None,
|
38
|
-
) ->
|
34
|
+
) -> T:
|
39
35
|
"""Load a set of settings from the `.env` file."""
|
40
36
|
path = get_root(path=path).joinpath(".env")
|
41
37
|
if not path.exists():
|
utilities/random.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from random import Random, SystemRandom
|
4
|
-
from typing import TYPE_CHECKING
|
4
|
+
from typing import TYPE_CHECKING
|
5
5
|
|
6
6
|
if TYPE_CHECKING:
|
7
7
|
from collections.abc import Iterable
|
@@ -9,7 +9,6 @@ if TYPE_CHECKING:
|
|
9
9
|
from utilities.types import Seed
|
10
10
|
|
11
11
|
|
12
|
-
_T = TypeVar("_T")
|
13
12
|
SYSTEM_RANDOM = SystemRandom()
|
14
13
|
|
15
14
|
|
@@ -54,7 +53,7 @@ def get_state(*, seed: Seed | None = None) -> Random:
|
|
54
53
|
|
55
54
|
|
56
55
|
##
|
57
|
-
def shuffle(iterable: Iterable[
|
56
|
+
def shuffle[T](iterable: Iterable[T], /, *, seed: Seed | None = None) -> list[T]:
|
58
57
|
"""Shuffle an iterable."""
|
59
58
|
copy = list(iterable).copy()
|
60
59
|
state = get_state(seed=seed)
|