dycw-utilities 0.138.3__py3-none-any.whl → 0.138.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.138.3
3
+ Version: 0.138.5
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=Tsb-lVma-ORGyB_i5MZSmb8W5vzY38b-c9EWIKbjGng,60
1
+ utilities/__init__.py,sha256=8_NgkEVVSVQiH0KSjlNRZwW_d9Lb2cnfj8c1HrNPmGA,60
2
2
  utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
3
3
  utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
4
4
  utilities/asyncio.py,sha256=dcGeKQzjLBXxKzZkVIk5oZsFXEcynVbRB9iNB5XEDZk,38526
@@ -20,7 +20,6 @@ utilities/fpdf2.py,sha256=776PkEX5xEK-whFOzqaVaQVHPy1Xf01kCSyj7TEp80g,1886
20
20
  utilities/functions.py,sha256=qefAfW0zz7OEiRuBtKF-3tI3NaufcwAULRIFv24gZ2Q,28533
21
21
  utilities/functools.py,sha256=I00ru2gQPakZw2SHVeKIKXfTv741655s6HI0lUoE0D4,1552
22
22
  utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
23
- utilities/git.py,sha256=oi7-_l5e9haSANSCvQw25ufYGoNahuUPHAZ6114s3JQ,1191
24
23
  utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
25
24
  utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
26
25
  utilities/hypothesis.py,sha256=2VJy9Pxqjy4_ldAwZAR6XEGuG2ZmAN1ycpA_VIGnKew,38819
@@ -43,7 +42,7 @@ utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
43
42
  utilities/orjson.py,sha256=WWV2QukCIuwT8OAOtmKhLhxezXPVbeA_fQCucmGmbRA,37106
44
43
  utilities/os.py,sha256=yMNAKMyY8oFgQ1yN3TQYnwa5-A_FXz4tCDbhIctQHSs,3736
45
44
  utilities/parse.py,sha256=JcJn5yXKhIWXBCwgBdPsyu7Hvcuw6kyEdqvaebCaI9k,17951
46
- utilities/pathlib.py,sha256=jCFPZm4rBKylEva9wDVTwQlTTVKMepu92WrTpoGa438,3248
45
+ utilities/pathlib.py,sha256=yE85i5nlXXdW9HFE2KzjmP2yyWhnZYQkMZ9TjQ8UQRk,4454
47
46
  utilities/period.py,sha256=6jEff_qAiE7xdFaQ1DnKgNf10D2wHhzt7hQXCBoKlgc,6842
48
47
  utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
49
48
  utilities/platform.py,sha256=5uCKRf_ij7ukJDcbnNfhY2ay9fbrpiNLRO1t2QvcwqQ,2825
@@ -55,7 +54,7 @@ utilities/psutil.py,sha256=0j4YxtVb8VjaaKKiHg6UEK95SUPkEcENgPtLgPJsNv0,3760
55
54
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
55
  utilities/pydantic.py,sha256=CmxCi4sukeHM3JGjJ1Rbp8UAvcx4MZapLg10mFYJ-nk,1771
57
56
  utilities/pyinstrument.py,sha256=HrTGJ2niUAHUFMSN3im9BeedC0faq2DqoFccDxPpsP8,884
58
- utilities/pytest.py,sha256=vv5ZpePZS6pBxl5jY3XQR5SnAhgd4f0sSMyRsvHWg60,8086
57
+ utilities/pytest.py,sha256=f6Yl2vHimin2Ulg0aUsNhdWmf6dC1Hs6nDTFOCOpRJI,8029
59
58
  utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
60
59
  utilities/python_dotenv.py,sha256=dYooRYwqrvhSoZWuiVbCiKUWiS-M5b5yv2zDWGYPEvI,3209
61
60
  utilities/random.py,sha256=YWYzWxQDeyJRiuHGnO1OxF6dDucpq7qc1tH_ealwCRg,4130
@@ -88,7 +87,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
88
87
  utilities/whenever.py,sha256=R5d9UCNCdAOyjwLUmfH2Vn8Ykee8OHQi2skRTFfbZMM,20492
89
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
90
89
  utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
91
- dycw_utilities-0.138.3.dist-info/METADATA,sha256=7JJYj8lFUbH2lDMsYk53t5e0mvDzRG8mD2j1gwSGASA,1638
92
- dycw_utilities-0.138.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
- dycw_utilities-0.138.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.138.3.dist-info/RECORD,,
90
+ dycw_utilities-0.138.5.dist-info/METADATA,sha256=49xR0EeDJd73mcxgxDir4zIZrsvAyW3Cb-iorojBolY,1638
91
+ dycw_utilities-0.138.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.138.5.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.138.5.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.138.3"
3
+ __version__ = "0.138.5"
utilities/pathlib.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
- from contextlib import contextmanager, suppress
4
+ from contextlib import contextmanager
5
5
  from dataclasses import dataclass
6
6
  from itertools import chain
7
7
  from os import chdir
@@ -11,6 +11,7 @@ from re import IGNORECASE, search
11
11
  from subprocess import PIPE, CalledProcessError, check_output
12
12
  from typing import TYPE_CHECKING, assert_never, overload, override
13
13
 
14
+ from utilities.errors import ImpossibleCaseError
14
15
  from utilities.sentinel import Sentinel, sentinel
15
16
 
16
17
  if TYPE_CHECKING:
@@ -73,23 +74,50 @@ def get_path(
73
74
  def get_root(*, path: MaybeCallablePathLike | None = None) -> Path:
74
75
  """Get the root of a path."""
75
76
  path = get_path(path=path)
77
+ path_dir = path.parent if path.is_file() else path
76
78
  try:
77
79
  output = check_output(
78
- ["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=path, text=True
80
+ ["git", "rev-parse", "--show-toplevel"],
81
+ stderr=PIPE,
82
+ cwd=path_dir,
83
+ text=True,
79
84
  )
80
85
  except CalledProcessError as error:
81
86
  # newer versions of git report "Not a git repository", whilst older
82
87
  # versions report "not a git repository"
83
88
  if not search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
84
89
  raise # pragma: no cover
90
+ root_git = None
85
91
  else:
86
- return Path(output.strip("\n"))
87
- all_paths = list(chain([path], path.parents))
88
- with suppress(StopIteration):
89
- return next(
90
- p for p in all_paths if any(p_i.name == ".envrc" for p_i in p.iterdir())
92
+ root_git = Path(output.strip("\n")).resolve()
93
+ all_paths = list(chain([path_dir], path_dir.parents))
94
+ try:
95
+ root_envrc = next(
96
+ p.resolve()
97
+ for p in all_paths
98
+ if any(p_i.name == ".envrc" for p_i in p.iterdir())
91
99
  )
92
- raise GetRootError(path=path)
100
+ except StopIteration:
101
+ root_envrc = None
102
+ match root_git, root_envrc:
103
+ case None, None:
104
+ raise GetRootError(path=path)
105
+ case Path(), None:
106
+ return root_git
107
+ case None, Path():
108
+ return root_envrc
109
+ case Path(), Path():
110
+ if root_git == root_envrc:
111
+ return root_git
112
+ if is_sub_path(root_git, root_envrc, strict=True):
113
+ return root_git
114
+ if is_sub_path(root_envrc, root_git, strict=True):
115
+ return root_envrc
116
+ raise ImpossibleCaseError( # pragma: no cover
117
+ case=[f"{root_git=}", f"{root_envrc=}"]
118
+ )
119
+ case _ as never:
120
+ assert_never(never)
93
121
 
94
122
 
95
123
  @dataclass(kw_only=True, slots=True)
@@ -104,6 +132,15 @@ class GetRootError(Exception):
104
132
  ##
105
133
 
106
134
 
135
+ def is_sub_path(x: PathLike, y: PathLike, /, *, strict: bool = False) -> bool:
136
+ """Check if a path is a sub path of another."""
137
+ x, y = [Path(i).resolve() for i in [x, y]]
138
+ return x.is_relative_to(y) and not (strict and y.is_relative_to(x))
139
+
140
+
141
+ ##
142
+
143
+
107
144
  def list_dir(path: PathLike, /) -> Sequence[Path]:
108
145
  """List the contents of a directory."""
109
146
  return sorted(Path(path).iterdir())
@@ -123,4 +160,12 @@ def temp_cwd(path: PathLike, /) -> Iterator[None]:
123
160
  chdir(prev)
124
161
 
125
162
 
126
- __all__ = ["PWD", "ensure_suffix", "expand_path", "get_path", "list_dir", "temp_cwd"]
163
+ __all__ = [
164
+ "PWD",
165
+ "ensure_suffix",
166
+ "expand_path",
167
+ "get_path",
168
+ "is_sub_path",
169
+ "list_dir",
170
+ "temp_cwd",
171
+ ]
utilities/pytest.py CHANGED
@@ -12,9 +12,8 @@ from whenever import ZonedDateTime
12
12
 
13
13
  from utilities.atomicwrites import writer
14
14
  from utilities.functools import cache
15
- from utilities.git import get_repo_root
16
15
  from utilities.hashlib import md5_hash
17
- from utilities.pathlib import ensure_suffix
16
+ from utilities.pathlib import ensure_suffix, get_root
18
17
  from utilities.platform import (
19
18
  IS_LINUX,
20
19
  IS_MAC,
@@ -241,9 +240,7 @@ def _skipif_recent(*, root: PathLike | None = None, delta: TimeDelta = SECOND) -
241
240
 
242
241
  def _get_path(*, root: PathLike | None = None) -> Path:
243
242
  if root is None:
244
- root_use = get_repo_root().joinpath( # pragma: no cover
245
- ".pytest_cache", "throttle"
246
- )
243
+ root_use = get_root().joinpath(".pytest_cache", "throttle") # pragma: no cover
247
244
  else:
248
245
  root_use = root
249
246
  return Path(root_use, _md5_hash_cached(_get_name()))
utilities/git.py DELETED
@@ -1,40 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
- 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
8
-
9
- from utilities.pathlib import PWD
10
-
11
- if TYPE_CHECKING:
12
- from utilities.types import PathLike
13
-
14
-
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
-
30
-
31
- @dataclass(kw_only=True, slots=True)
32
- class GetRepoRootError(Exception):
33
- cwd: PathLike
34
-
35
- @override
36
- def __str__(self) -> str:
37
- return f"Path is not part of a `git` repository: {self.cwd}"
38
-
39
-
40
- __all__ = ["GetRepoRootError", "get_repo_root"]