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.
Files changed (103) hide show
  1. dycw_utilities-0.175.17.dist-info/METADATA +34 -0
  2. dycw_utilities-0.175.17.dist-info/RECORD +103 -0
  3. dycw_utilities-0.175.17.dist-info/WHEEL +4 -0
  4. dycw_utilities-0.175.17.dist-info/entry_points.txt +4 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +14 -14
  7. utilities/asyncio.py +350 -819
  8. utilities/atomicwrites.py +18 -6
  9. utilities/atools.py +77 -22
  10. utilities/cachetools.py +24 -29
  11. utilities/click.py +393 -237
  12. utilities/concurrent.py +8 -11
  13. utilities/contextlib.py +216 -17
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +83 -118
  17. utilities/docker.py +293 -0
  18. utilities/enum.py +26 -23
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +29 -65
  21. utilities/fpdf2.py +3 -3
  22. utilities/functions.py +169 -416
  23. utilities/functools.py +18 -19
  24. utilities/git.py +9 -30
  25. utilities/grp.py +28 -0
  26. utilities/gzip.py +31 -0
  27. utilities/http.py +3 -2
  28. utilities/hypothesis.py +738 -589
  29. utilities/importlib.py +17 -1
  30. utilities/inflect.py +25 -0
  31. utilities/iterables.py +194 -262
  32. utilities/jinja2.py +148 -0
  33. utilities/json.py +70 -0
  34. utilities/libcst.py +38 -17
  35. utilities/lightweight_charts.py +5 -9
  36. utilities/logging.py +345 -543
  37. utilities/math.py +18 -13
  38. utilities/memory_profiler.py +11 -15
  39. utilities/more_itertools.py +200 -131
  40. utilities/operator.py +33 -29
  41. utilities/optuna.py +6 -6
  42. utilities/orjson.py +272 -137
  43. utilities/os.py +61 -4
  44. utilities/parse.py +59 -61
  45. utilities/pathlib.py +281 -40
  46. utilities/permissions.py +298 -0
  47. utilities/pickle.py +2 -2
  48. utilities/platform.py +24 -5
  49. utilities/polars.py +1214 -430
  50. utilities/polars_ols.py +1 -1
  51. utilities/postgres.py +408 -0
  52. utilities/pottery.py +113 -26
  53. utilities/pqdm.py +10 -11
  54. utilities/psutil.py +6 -57
  55. utilities/pwd.py +28 -0
  56. utilities/pydantic.py +4 -54
  57. utilities/pydantic_settings.py +240 -0
  58. utilities/pydantic_settings_sops.py +76 -0
  59. utilities/pyinstrument.py +8 -10
  60. utilities/pytest.py +227 -121
  61. utilities/pytest_plugins/__init__.py +1 -0
  62. utilities/pytest_plugins/pytest_randomly.py +23 -0
  63. utilities/pytest_plugins/pytest_regressions.py +56 -0
  64. utilities/pytest_regressions.py +26 -46
  65. utilities/random.py +13 -9
  66. utilities/re.py +58 -28
  67. utilities/redis.py +401 -550
  68. utilities/scipy.py +1 -1
  69. utilities/sentinel.py +10 -0
  70. utilities/shelve.py +4 -1
  71. utilities/shutil.py +25 -0
  72. utilities/slack_sdk.py +36 -106
  73. utilities/sqlalchemy.py +502 -473
  74. utilities/sqlalchemy_polars.py +38 -94
  75. utilities/string.py +2 -3
  76. utilities/subprocess.py +1572 -0
  77. utilities/tempfile.py +86 -4
  78. utilities/testbook.py +50 -0
  79. utilities/text.py +165 -42
  80. utilities/timer.py +37 -65
  81. utilities/traceback.py +158 -929
  82. utilities/types.py +146 -116
  83. utilities/typing.py +531 -71
  84. utilities/tzdata.py +1 -53
  85. utilities/tzlocal.py +6 -23
  86. utilities/uuid.py +43 -5
  87. utilities/version.py +27 -26
  88. utilities/whenever.py +1776 -386
  89. utilities/zoneinfo.py +84 -22
  90. dycw_utilities-0.129.10.dist-info/METADATA +0 -241
  91. dycw_utilities-0.129.10.dist-info/RECORD +0 -96
  92. dycw_utilities-0.129.10.dist-info/WHEEL +0 -4
  93. dycw_utilities-0.129.10.dist-info/licenses/LICENSE +0 -21
  94. utilities/datetime.py +0 -1409
  95. utilities/eventkit.py +0 -402
  96. utilities/loguru.py +0 -144
  97. utilities/luigi.py +0 -228
  98. utilities/period.py +0 -324
  99. utilities/pyrsistent.py +0 -89
  100. utilities/python_dotenv.py +0 -105
  101. utilities/streamlit.py +0 -105
  102. utilities/sys.py +0 -87
  103. 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, TypeVar, cast, overload, override
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
- _T = TypeVar("_T")
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[[TCallable], TCallable]", _cache)
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: TCallable, /, *, max_size: int = ..., typed: bool = ...
28
- ) -> TCallable: ...
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 = ..., typed: bool = ...
32
- ) -> Callable[[TCallable], TCallable]: ...
33
- def lru_cache(
34
- func: TCallable | None = None, /, *, max_size: int = 128, typed: bool = False
35
- ) -> TCallable | Callable[[TCallable], TCallable]:
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[[TCallable], TCallable]", result)
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[_T]): # noqa: N801
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) -> _T:
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 re import IGNORECASE, search
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 utilities.pathlib import PWD
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
- def get_repo_root(*, path: PathLike = PWD) -> Path:
16
- """Get the repo root."""
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
- @override
36
- def __str__(self) -> str:
37
- return f"Path is not part of a `git` repository: {self.cwd}"
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__ = ["GetRepoRootError", "get_repo_root"]
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
- @contextmanager
25
+ @enhanced_context_manager
25
26
  def yield_connection(
26
27
  host: str, /, *, timeout: float | None = None
27
28
  ) -> Iterator[HTTPSConnection]: