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/functools.py
CHANGED
|
@@ -3,40 +3,39 @@ from __future__ import annotations
|
|
|
3
3
|
from functools import cache as _cache
|
|
4
4
|
from functools import lru_cache as _lru_cache
|
|
5
5
|
from functools import partial as _partial
|
|
6
|
-
from typing import TYPE_CHECKING, Any,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast, overload, override
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from collections.abc import Callable
|
|
10
10
|
|
|
11
|
-
from utilities.types import TCallable
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def cache(func: TCallable, /) -> TCallable:
|
|
12
|
+
def cache[F: Callable](func: F, /) -> F:
|
|
17
13
|
"""Typed version of `cache`."""
|
|
18
|
-
typed_cache = cast("Callable[[
|
|
14
|
+
typed_cache = cast("Callable[[F], F]", _cache)
|
|
19
15
|
return typed_cache(func)
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
##
|
|
23
19
|
|
|
24
20
|
|
|
21
|
+
_MAX_SIZE = 128
|
|
22
|
+
|
|
23
|
+
|
|
25
24
|
@overload
|
|
26
|
-
def lru_cache(
|
|
27
|
-
func:
|
|
28
|
-
) ->
|
|
25
|
+
def lru_cache[F: Callable](
|
|
26
|
+
func: F, /, *, max_size: int = _MAX_SIZE, typed: bool = False
|
|
27
|
+
) -> F: ...
|
|
29
28
|
@overload
|
|
30
|
-
def lru_cache(
|
|
31
|
-
func: None = None, /, *, max_size: int =
|
|
32
|
-
) -> Callable[[
|
|
33
|
-
def lru_cache(
|
|
34
|
-
func:
|
|
35
|
-
) ->
|
|
29
|
+
def lru_cache[F: Callable](
|
|
30
|
+
func: None = None, /, *, max_size: int = _MAX_SIZE, typed: bool = False
|
|
31
|
+
) -> Callable[[F], F]: ...
|
|
32
|
+
def lru_cache[F: Callable](
|
|
33
|
+
func: F | None = None, /, *, max_size: int = _MAX_SIZE, typed: bool = False
|
|
34
|
+
) -> F | Callable[[F], F]:
|
|
36
35
|
"""Typed version of `lru_cache`."""
|
|
37
36
|
if func is None:
|
|
38
37
|
result = partial(lru_cache, max_size=max_size, typed=typed)
|
|
39
|
-
return cast("Callable[[
|
|
38
|
+
return cast("Callable[[F], F]", result)
|
|
40
39
|
wrapped = _lru_cache(maxsize=max_size, typed=typed)(func)
|
|
41
40
|
return cast("Any", wrapped)
|
|
42
41
|
|
|
@@ -44,11 +43,11 @@ def lru_cache(
|
|
|
44
43
|
##
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
class partial(_partial[
|
|
46
|
+
class partial[T](_partial[T]): # noqa: N801
|
|
48
47
|
"""Partial which accepts Ellipsis for positional arguments."""
|
|
49
48
|
|
|
50
49
|
@override
|
|
51
|
-
def __call__(self, *args: Any, **kwargs: Any) ->
|
|
50
|
+
def __call__(self, *args: Any, **kwargs: Any) -> T:
|
|
52
51
|
iter_args = iter(args)
|
|
53
52
|
head = (next(iter_args) if arg is ... else arg for arg in self.args)
|
|
54
53
|
return self.func(*head, *iter_args, **{**self.keywords, **kwargs})
|
utilities/git.py
CHANGED
|
@@ -1,40 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from
|
|
6
|
-
from subprocess import PIPE, CalledProcessError, check_output
|
|
7
|
-
from typing import TYPE_CHECKING, override
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
8
5
|
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from utilities.types import PathLike
|
|
6
|
+
from git import Repo
|
|
13
7
|
|
|
8
|
+
from utilities.pathlib import to_path
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
try:
|
|
18
|
-
output = check_output(
|
|
19
|
-
["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=path, text=True
|
|
20
|
-
)
|
|
21
|
-
except CalledProcessError as error:
|
|
22
|
-
# newer versions of git report "Not a git repository", whilst older
|
|
23
|
-
# versions report "not a git repository"
|
|
24
|
-
if search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
|
|
25
|
-
raise GetRepoRootError(cwd=path) from error
|
|
26
|
-
raise # pragma: no cover
|
|
27
|
-
else:
|
|
28
|
-
return Path(output.strip("\n"))
|
|
29
|
-
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from utilities.types import MaybeCallablePathLike
|
|
30
12
|
|
|
31
|
-
@dataclass(kw_only=True, slots=True)
|
|
32
|
-
class GetRepoRootError(Exception):
|
|
33
|
-
cwd: PathLike
|
|
34
13
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
14
|
+
def get_repo(path: MaybeCallablePathLike = Path.cwd, /) -> Repo:
|
|
15
|
+
"""Get the repo object."""
|
|
16
|
+
return Repo(to_path(path), search_parent_directories=True)
|
|
38
17
|
|
|
39
18
|
|
|
40
|
-
__all__ = ["
|
|
19
|
+
__all__ = ["get_repo"]
|
utilities/grp.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import assert_never
|
|
4
|
+
|
|
5
|
+
from utilities.os import EFFECTIVE_GROUP_ID
|
|
6
|
+
from utilities.platform import SYSTEM
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_gid_name(gid: int, /) -> str | None:
|
|
10
|
+
"""Get the name of a group."""
|
|
11
|
+
match SYSTEM:
|
|
12
|
+
case "windows": # skipif-not-windows
|
|
13
|
+
return None
|
|
14
|
+
case "mac" | "linux":
|
|
15
|
+
from grp import getgrgid
|
|
16
|
+
|
|
17
|
+
return getgrgid(gid).gr_name
|
|
18
|
+
case never:
|
|
19
|
+
assert_never(never)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
ROOT_GROUP_NAME = get_gid_name(0)
|
|
23
|
+
EFFECTIVE_GROUP_NAME = (
|
|
24
|
+
None if EFFECTIVE_GROUP_ID is None else get_gid_name(EFFECTIVE_GROUP_ID)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["EFFECTIVE_GROUP_NAME", "ROOT_GROUP_NAME", "get_gid_name"]
|
utilities/gzip.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import gzip
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from utilities.atomicwrites import writer
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from utilities.types import PathLike
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def read_binary(path: PathLike, /, *, decompress: bool = False) -> bytes:
|
|
14
|
+
"""Read a byte string from disk."""
|
|
15
|
+
path = Path(path)
|
|
16
|
+
if decompress:
|
|
17
|
+
with gzip.open(path) as gz:
|
|
18
|
+
return gz.read()
|
|
19
|
+
else:
|
|
20
|
+
return path.read_bytes()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def write_binary(
|
|
24
|
+
data: bytes, path: PathLike, /, *, compress: bool = False, overwrite: bool = False
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Write a byte string to disk."""
|
|
27
|
+
with writer(path, compress=compress, overwrite=overwrite) as temp:
|
|
28
|
+
_ = temp.write_bytes(data)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ["read_binary", "write_binary"]
|
utilities/http.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from contextlib import contextmanager
|
|
4
3
|
from http.client import HTTPSConnection
|
|
5
4
|
from ipaddress import IPv4Address
|
|
6
5
|
from typing import TYPE_CHECKING
|
|
7
6
|
|
|
7
|
+
from utilities.contextlib import enhanced_context_manager
|
|
8
|
+
|
|
8
9
|
if TYPE_CHECKING:
|
|
9
10
|
from collections.abc import Iterator
|
|
10
11
|
|
|
@@ -21,7 +22,7 @@ def get_public_ip(*, timeout: float | None = None) -> IPv4Address:
|
|
|
21
22
|
##
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
@
|
|
25
|
+
@enhanced_context_manager
|
|
25
26
|
def yield_connection(
|
|
26
27
|
host: str, /, *, timeout: float | None = None
|
|
27
28
|
) -> Iterator[HTTPSConnection]:
|