dycw-utilities 0.127.0__py3-none-any.whl → 0.128.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.127.0.dist-info → dycw_utilities-0.128.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.127.0.dist-info → dycw_utilities-0.128.0.dist-info}/RECORD +14 -15
- utilities/__init__.py +1 -1
- utilities/datetime.py +0 -8
- utilities/hypothesis.py +1 -11
- utilities/logging.py +9 -12
- utilities/pathlib.py +72 -13
- utilities/pyinstrument.py +6 -4
- utilities/pytest_regressions.py +2 -2
- utilities/python_dotenv.py +10 -6
- utilities/types.py +2 -2
- utilities/version.py +0 -8
- utilities/git.py +0 -93
- {dycw_utilities-0.127.0.dist-info → dycw_utilities-0.128.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.127.0.dist-info → dycw_utilities-0.128.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=eWwSSpaFIxxRG_Er2j_6n618a-HRkBuT-C-hli9i1bM,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/asyncio.py,sha256=wKxwNnxdWxsiy5U0b1F3UgpWRHlPKM0y_OcmURzqxR8,51396
|
4
4
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
@@ -11,7 +11,7 @@ utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
|
11
11
|
utilities/cryptography.py,sha256=_CiK_K6c_-uQuUhsUNjNjTL-nqxAh4_1zTfS11Xe120,972
|
12
12
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
13
13
|
utilities/dataclasses.py,sha256=iiC1wpGXWhaocIikzwBt8bbLWyImoUlOlcDZJGejaIg,33011
|
14
|
-
utilities/datetime.py,sha256=
|
14
|
+
utilities/datetime.py,sha256=aiPh2OZK2g9gn4yEeSO0lODOmvx8U_rGn6XeSzyk4VY,38738
|
15
15
|
utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
|
16
16
|
utilities/errors.py,sha256=nC7ZYtxxDBMfrTHtT_MByBfup_wfGQFRo3eDt-0ZPe8,1045
|
17
17
|
utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
|
@@ -20,17 +20,16 @@ utilities/fpdf2.py,sha256=y1NGXR5chWqLXWpewGV3hlRGMr_5yV1lVRkPBhPEgJI,1843
|
|
20
20
|
utilities/functions.py,sha256=jgt592voaHNtX56qX0SRvFveVCRmSIxCZmqvpLZCnY8,27305
|
21
21
|
utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
|
22
22
|
utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
23
|
-
utilities/git.py,sha256=wpt5dZ5Oi5931pN24_VLZYaQOvmR0OcQuVtgHzFUN1k,2359
|
24
23
|
utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
|
25
24
|
utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
|
26
|
-
utilities/hypothesis.py,sha256=
|
25
|
+
utilities/hypothesis.py,sha256=UnUMJmeqwJuK7uyUqw_i3opUYzVKud4RMG0RMOSRBQY,44463
|
27
26
|
utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
28
27
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
29
28
|
utilities/iterables.py,sha256=mDqw2_0MUVp-P8FklgcaVTi2TXduH0MxbhTDzzhSBho,44915
|
30
29
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
31
30
|
utilities/libcst.py,sha256=Jto5ppzRzsxn4AD32IS8n0lbgLYXwsVJB6EY8giNZyY,4974
|
32
31
|
utilities/lightweight_charts.py,sha256=0xNfcsrgFI0R9xL25LtSm-W5yhfBI93qQNT6HyaXAhg,2769
|
33
|
-
utilities/logging.py,sha256=
|
32
|
+
utilities/logging.py,sha256=a99gX9oQUe_Oxs5rDtTwUVuOwhRyeO_GfoFNKVaEny0,25641
|
34
33
|
utilities/loguru.py,sha256=MEMQVWrdECxk1e3FxGzmOf21vWT9j8CAir98SEXFKPA,3809
|
35
34
|
utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
|
36
35
|
utilities/math.py,sha256=-mQgbah-dPJwOEWf3SonrFoVZ2AVxMgpeQ3dfVa-oJA,26764
|
@@ -43,7 +42,7 @@ utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
|
|
43
42
|
utilities/orjson.py,sha256=AvPFxzJdxC-3PBID3cqdiMyN8FeC7aW9QUgGwbvKuAM,36948
|
44
43
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
45
44
|
utilities/parse.py,sha256=vsZ2jf_ceSI_Kta9titixufysJaVXh0Whjz1T4awJZw,18938
|
46
|
-
utilities/pathlib.py,sha256=
|
45
|
+
utilities/pathlib.py,sha256=0cQpqmZs-Pe2693xZwGFApq-B9mADqhX-pclf_5iLco,3041
|
47
46
|
utilities/period.py,sha256=o4wXYEXVlFomop4-Ra4L0yRP4i99NZFjIe_fa7NdZck,11024
|
48
47
|
utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
|
49
48
|
utilities/platform.py,sha256=48IOKx1IC6ZJXWG-b56ZQptITcNFhWRjELW72o2dGTA,2398
|
@@ -54,11 +53,11 @@ utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
|
|
54
53
|
utilities/psutil.py,sha256=RtbLKOoIJhqrJmEoHDBVeSD-KPzshtS0FtRXBP9_w2s,3751
|
55
54
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
55
|
utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
|
57
|
-
utilities/pyinstrument.py,sha256=
|
56
|
+
utilities/pyinstrument.py,sha256=O2dngLsmUUnpMtW1eN3OiM0rGQNBIlXSvZmym_jAsvU,904
|
58
57
|
utilities/pyrsistent.py,sha256=wVOVIe_68AAaa-lUE9y-TEzDawVp1uEIc_zfoDgr5ww,2287
|
59
58
|
utilities/pytest.py,sha256=KoHSwJbIY2CHtFUlUr_gnEk7z1DVTaldl8RDQ4tDkG4,7837
|
60
|
-
utilities/pytest_regressions.py,sha256
|
61
|
-
utilities/python_dotenv.py,sha256=
|
59
|
+
utilities/pytest_regressions.py,sha256=YI55B7EtLjhz7zPJZ6NK9bWrxrKCKabWZJe1cwcbA5o,5082
|
60
|
+
utilities/python_dotenv.py,sha256=edXsvHZhZnYeqfMfrsRRpj7_9eJI6uizh3xLx8Q9B3w,3228
|
62
61
|
utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
|
63
62
|
utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
|
64
63
|
utilities/redis.py,sha256=EZgqWeoGpvN-BfCQL93F3rYlfB4U_zhzHCBuZpDmKpo,37157
|
@@ -80,17 +79,17 @@ utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
|
|
80
79
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
81
80
|
utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
|
82
81
|
utilities/traceback.py,sha256=Jg7HS3AwQ-W-msdwHp22_PSHZcR54PbmsSf115B6TSM,27435
|
83
|
-
utilities/types.py,sha256=
|
82
|
+
utilities/types.py,sha256=gP04CcCOyFrG7BgblVCsrrChiuO2x842NDVW-GF7odo,18370
|
84
83
|
utilities/typing.py,sha256=H6ysJkI830aRwLsMKz0SZIw4cpcsm7d6KhQOwr-SDh0,13817
|
85
84
|
utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
|
86
85
|
utilities/tzlocal.py,sha256=3upDNFBvGh1l9njmLR2z2S6K6VxQSb7QizYGUbAH3JU,960
|
87
86
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
88
|
-
utilities/version.py,sha256=
|
87
|
+
utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
|
89
88
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
90
89
|
utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
|
91
90
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
92
91
|
utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
|
93
|
-
dycw_utilities-0.
|
94
|
-
dycw_utilities-0.
|
95
|
-
dycw_utilities-0.
|
96
|
-
dycw_utilities-0.
|
92
|
+
dycw_utilities-0.128.0.dist-info/METADATA,sha256=BUxspfSeFNdeLBrh89gZYdXyqBuAr4yzLZ_ylcVOBlI,12803
|
93
|
+
dycw_utilities-0.128.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
94
|
+
dycw_utilities-0.128.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
95
|
+
dycw_utilities-0.128.0.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/datetime.py
CHANGED
@@ -509,14 +509,6 @@ def get_datetime(*, datetime: MaybeCallableDateTime) -> dt.datetime: ...
|
|
509
509
|
def get_datetime(*, datetime: None) -> None: ...
|
510
510
|
@overload
|
511
511
|
def get_datetime(*, datetime: Sentinel) -> Sentinel: ...
|
512
|
-
@overload
|
513
|
-
def get_datetime(
|
514
|
-
*, datetime: MaybeCallableDateTime | Sentinel
|
515
|
-
) -> dt.datetime | Sentinel: ...
|
516
|
-
@overload
|
517
|
-
def get_datetime(
|
518
|
-
*, datetime: MaybeCallableDateTime | None | Sentinel = sentinel
|
519
|
-
) -> dt.datetime | None | Sentinel: ...
|
520
512
|
def get_datetime(
|
521
513
|
*, datetime: MaybeCallableDateTime | None | Sentinel = sentinel
|
522
514
|
) -> dt.datetime | None | Sentinel:
|
utilities/hypothesis.py
CHANGED
@@ -506,13 +506,7 @@ def floats_extra(
|
|
506
506
|
|
507
507
|
|
508
508
|
@composite
|
509
|
-
def git_repos(
|
510
|
-
draw: DrawFn,
|
511
|
-
/,
|
512
|
-
*,
|
513
|
-
branch: MaybeSearchStrategy[str | None] = None,
|
514
|
-
remote: MaybeSearchStrategy[str | None] = None,
|
515
|
-
) -> Path:
|
509
|
+
def git_repos(draw: DrawFn, /) -> Path:
|
516
510
|
path = draw(temp_paths())
|
517
511
|
with temp_cwd(path):
|
518
512
|
_ = check_call(["git", "init", "-b", "master"])
|
@@ -525,10 +519,6 @@ def git_repos(
|
|
525
519
|
_ = check_call(["git", "commit", "-m", "add"])
|
526
520
|
_ = check_call(["git", "rm", file_str])
|
527
521
|
_ = check_call(["git", "commit", "-m", "rm"])
|
528
|
-
if (branch_ := draw2(draw, branch)) is not None:
|
529
|
-
_ = check_call(["git", "checkout", "-b", branch_])
|
530
|
-
if (remote_ := draw2(draw, remote)) is not None:
|
531
|
-
_ = check_call(["git", "remote", "add", "origin", remote_])
|
532
522
|
return path
|
533
523
|
|
534
524
|
|
utilities/logging.py
CHANGED
@@ -46,9 +46,8 @@ from utilities.datetime import (
|
|
46
46
|
serialize_compact,
|
47
47
|
)
|
48
48
|
from utilities.errors import ImpossibleCaseError
|
49
|
-
from utilities.git import get_repo_root
|
50
49
|
from utilities.iterables import OneEmptyError, always_iterable, one
|
51
|
-
from utilities.pathlib import ensure_suffix,
|
50
|
+
from utilities.pathlib import ensure_suffix, get_path, get_root
|
52
51
|
from utilities.reprlib import (
|
53
52
|
RICH_EXPAND_ALL,
|
54
53
|
RICH_INDENT_SIZE,
|
@@ -68,9 +67,9 @@ if TYPE_CHECKING:
|
|
68
67
|
from utilities.types import (
|
69
68
|
LoggerOrName,
|
70
69
|
LogLevel,
|
70
|
+
MaybeCallablePathLike,
|
71
71
|
MaybeIterable,
|
72
72
|
PathLike,
|
73
|
-
PathLikeOrCallable,
|
74
73
|
)
|
75
74
|
from utilities.version import MaybeCallableVersionLike
|
76
75
|
|
@@ -383,10 +382,10 @@ class StandaloneFileHandler(Handler):
|
|
383
382
|
|
384
383
|
@override
|
385
384
|
def __init__(
|
386
|
-
self, *, level: int = NOTSET, path:
|
385
|
+
self, *, level: int = NOTSET, path: MaybeCallablePathLike | None = None
|
387
386
|
) -> None:
|
388
387
|
super().__init__(level=level)
|
389
|
-
self._path = path
|
388
|
+
self._path = get_path(path=path)
|
390
389
|
|
391
390
|
@override
|
392
391
|
def emit(self, record: LogRecord) -> None:
|
@@ -394,10 +393,8 @@ class StandaloneFileHandler(Handler):
|
|
394
393
|
from utilities.tzlocal import get_now_local
|
395
394
|
|
396
395
|
try:
|
397
|
-
path = (
|
398
|
-
|
399
|
-
.joinpath(serialize_compact(get_now_local()))
|
400
|
-
.with_suffix(".txt")
|
396
|
+
path = self._path.joinpath(serialize_compact(get_now_local())).with_suffix(
|
397
|
+
".txt"
|
401
398
|
)
|
402
399
|
formatted = self.format(record)
|
403
400
|
with writer(path, overwrite=True) as temp, temp.open(mode="w") as fh:
|
@@ -473,7 +470,7 @@ class FilterForKeyError(Exception):
|
|
473
470
|
|
474
471
|
def get_default_logging_path() -> Path:
|
475
472
|
"""Get the logging default path."""
|
476
|
-
return
|
473
|
+
return get_root().joinpath(".logs")
|
477
474
|
|
478
475
|
|
479
476
|
##
|
@@ -520,7 +517,7 @@ def setup_logging(
|
|
520
517
|
console_level: LogLevel | None = "INFO",
|
521
518
|
console_filters: Iterable[_FilterType] | None = None,
|
522
519
|
console_fmt: str = "❯ {_zoned_datetime_str} | {name}:{funcName}:{lineno} | {message}", # noqa: RUF001
|
523
|
-
files_dir:
|
520
|
+
files_dir: MaybeCallablePathLike | None = get_default_logging_path,
|
524
521
|
files_when: _When = "D",
|
525
522
|
files_interval: int = 1,
|
526
523
|
files_backup_count: int = 10,
|
@@ -616,7 +613,7 @@ def setup_logging(
|
|
616
613
|
logger_use.addHandler(console_high_and_exc_handler)
|
617
614
|
|
618
615
|
# debug & info
|
619
|
-
directory =
|
616
|
+
directory = get_path(path=files_dir) # skipif-ci-and-windows
|
620
617
|
levels: list[LogLevel] = ["DEBUG", "INFO"] # skipif-ci-and-windows
|
621
618
|
for level, (subpath, files_or_plain_formatter) in product( # skipif-ci-and-windows
|
622
619
|
levels, [(Path(), files_formatter), (Path("plain"), plain_formatter)]
|
utilities/pathlib.py
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from
|
3
|
+
from collections.abc import Callable
|
4
|
+
from contextlib import contextmanager, suppress
|
5
|
+
from dataclasses import dataclass
|
4
6
|
from itertools import chain
|
5
7
|
from os import chdir
|
6
8
|
from pathlib import Path
|
7
|
-
from
|
9
|
+
from re import IGNORECASE, search
|
10
|
+
from subprocess import PIPE, CalledProcessError, check_output
|
11
|
+
from typing import TYPE_CHECKING, assert_never, overload, override
|
12
|
+
|
13
|
+
from utilities.sentinel import Sentinel, sentinel
|
8
14
|
|
9
15
|
if TYPE_CHECKING:
|
10
16
|
from collections.abc import Iterator, Sequence
|
11
17
|
|
12
|
-
from utilities.types import
|
18
|
+
from utilities.types import MaybeCallablePathLike, PathLike
|
13
19
|
|
14
20
|
PWD = Path.cwd()
|
15
21
|
|
@@ -25,20 +31,73 @@ def ensure_suffix(path: PathLike, suffix: str, /) -> Path:
|
|
25
31
|
return path.with_name(name)
|
26
32
|
|
27
33
|
|
28
|
-
|
29
|
-
"""List the contents of a directory."""
|
30
|
-
return sorted(Path(path).iterdir())
|
34
|
+
##
|
31
35
|
|
32
36
|
|
33
|
-
|
34
|
-
|
37
|
+
@overload
|
38
|
+
def get_path(*, path: MaybeCallablePathLike | None) -> Path: ...
|
39
|
+
@overload
|
40
|
+
def get_path(*, path: Sentinel) -> Sentinel: ...
|
41
|
+
def get_path(
|
42
|
+
*, path: MaybeCallablePathLike | None | Sentinel = sentinel
|
43
|
+
) -> Path | None | Sentinel:
|
44
|
+
"""Get the path."""
|
35
45
|
match path:
|
46
|
+
case Path() | Sentinel():
|
47
|
+
return path
|
48
|
+
case str():
|
49
|
+
return Path(path)
|
36
50
|
case None:
|
37
51
|
return Path.cwd()
|
38
|
-
case
|
39
|
-
return
|
40
|
-
case _:
|
41
|
-
|
52
|
+
case Callable() as func:
|
53
|
+
return get_path(path=func())
|
54
|
+
case _ as never:
|
55
|
+
assert_never(never)
|
56
|
+
|
57
|
+
|
58
|
+
##
|
59
|
+
|
60
|
+
|
61
|
+
def get_root(*, path: MaybeCallablePathLike | None = None) -> Path:
|
62
|
+
"""Get the root of a path."""
|
63
|
+
path = get_path(path=path)
|
64
|
+
try:
|
65
|
+
output = check_output(
|
66
|
+
["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=path, text=True
|
67
|
+
)
|
68
|
+
except CalledProcessError as error:
|
69
|
+
# newer versions of git report "Not a git repository", whilst older
|
70
|
+
# versions report "not a git repository"
|
71
|
+
if not search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
|
72
|
+
raise # pragma: no cover
|
73
|
+
else:
|
74
|
+
return Path(output.strip("\n"))
|
75
|
+
all_paths = list(chain([path], path.parents))
|
76
|
+
with suppress(StopIteration):
|
77
|
+
return next(
|
78
|
+
p for p in all_paths if any(p_i.name == ".envrc" for p_i in p.iterdir())
|
79
|
+
)
|
80
|
+
raise GetRootError(path=path)
|
81
|
+
|
82
|
+
|
83
|
+
@dataclass(kw_only=True, slots=True)
|
84
|
+
class GetRootError(Exception):
|
85
|
+
path: PathLike
|
86
|
+
|
87
|
+
@override
|
88
|
+
def __str__(self) -> str:
|
89
|
+
return f"Unable to determine root from {str(self.path)!r}"
|
90
|
+
|
91
|
+
|
92
|
+
##
|
93
|
+
|
94
|
+
|
95
|
+
def list_dir(path: PathLike, /) -> Sequence[Path]:
|
96
|
+
"""List the contents of a directory."""
|
97
|
+
return sorted(Path(path).iterdir())
|
98
|
+
|
99
|
+
|
100
|
+
##
|
42
101
|
|
43
102
|
|
44
103
|
@contextmanager
|
@@ -52,4 +111,4 @@ def temp_cwd(path: PathLike, /) -> Iterator[None]:
|
|
52
111
|
chdir(prev)
|
53
112
|
|
54
113
|
|
55
|
-
__all__ = ["ensure_suffix", "
|
114
|
+
__all__ = ["PWD", "ensure_suffix", "get_path", "list_dir", "temp_cwd"]
|
utilities/pyinstrument.py
CHANGED
@@ -7,23 +7,25 @@ from typing import TYPE_CHECKING
|
|
7
7
|
from pyinstrument.profiler import Profiler
|
8
8
|
|
9
9
|
from utilities.datetime import serialize_compact
|
10
|
-
from utilities.pathlib import
|
10
|
+
from utilities.pathlib import get_path
|
11
11
|
from utilities.tzlocal import get_now_local
|
12
12
|
|
13
13
|
if TYPE_CHECKING:
|
14
14
|
from collections.abc import Iterator
|
15
15
|
|
16
|
-
from utilities.types import
|
16
|
+
from utilities.types import MaybeCallablePathLike
|
17
17
|
|
18
18
|
|
19
19
|
@contextmanager
|
20
|
-
def profile(*, path:
|
20
|
+
def profile(*, path: MaybeCallablePathLike | None = Path.cwd) -> Iterator[None]:
|
21
21
|
"""Profile the contents of a block."""
|
22
22
|
from utilities.atomicwrites import writer
|
23
23
|
|
24
24
|
with Profiler() as profiler:
|
25
25
|
yield
|
26
|
-
filename =
|
26
|
+
filename = get_path(path=path).joinpath(
|
27
|
+
f"profile__{serialize_compact(get_now_local())}.html"
|
28
|
+
)
|
27
29
|
with writer(filename) as temp, temp.open(mode="w") as fh:
|
28
30
|
_ = fh.write(profiler.output_html())
|
29
31
|
|
utilities/pytest_regressions.py
CHANGED
@@ -10,8 +10,8 @@ from pytest import fixture
|
|
10
10
|
from pytest_regressions.file_regression import FileRegressionFixture
|
11
11
|
|
12
12
|
from utilities.functions import ensure_str
|
13
|
-
from utilities.git import get_repo_root
|
14
13
|
from utilities.operator import is_equal
|
14
|
+
from utilities.pathlib import get_root
|
15
15
|
from utilities.pytest import node_id_to_path
|
16
16
|
|
17
17
|
if TYPE_CHECKING:
|
@@ -153,7 +153,7 @@ def polars_regression(
|
|
153
153
|
|
154
154
|
def _get_path(request: FixtureRequest, /) -> Path:
|
155
155
|
tail = node_id_to_path(request.node.nodeid, head=_PATH_TESTS)
|
156
|
-
return
|
156
|
+
return get_root().joinpath(_PATH_TESTS, "regressions", tail)
|
157
157
|
|
158
158
|
|
159
159
|
__all__ = [
|
utilities/python_dotenv.py
CHANGED
@@ -2,29 +2,33 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from os import environ
|
5
|
+
from pathlib import Path
|
5
6
|
from typing import TYPE_CHECKING, override
|
6
7
|
|
7
8
|
from dotenv import dotenv_values
|
8
9
|
|
9
10
|
from utilities.dataclasses import _ParseDataClassMissingValuesError, parse_dataclass
|
10
|
-
from utilities.git import get_repo_root
|
11
11
|
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
12
|
-
from utilities.pathlib import
|
12
|
+
from utilities.pathlib import get_root
|
13
13
|
from utilities.reprlib import get_repr
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from collections.abc import Mapping
|
17
17
|
from collections.abc import Set as AbstractSet
|
18
|
-
from pathlib import Path
|
19
18
|
|
20
|
-
from utilities.types import
|
19
|
+
from utilities.types import (
|
20
|
+
MaybeCallablePathLike,
|
21
|
+
ParseObjectExtra,
|
22
|
+
StrMapping,
|
23
|
+
TDataclass,
|
24
|
+
)
|
21
25
|
|
22
26
|
|
23
27
|
def load_settings(
|
24
28
|
cls: type[TDataclass],
|
25
29
|
/,
|
26
30
|
*,
|
27
|
-
|
31
|
+
path: MaybeCallablePathLike | None = Path.cwd,
|
28
32
|
globalns: StrMapping | None = None,
|
29
33
|
localns: StrMapping | None = None,
|
30
34
|
warn_name_errors: bool = False,
|
@@ -33,7 +37,7 @@ def load_settings(
|
|
33
37
|
extra_parsers: ParseObjectExtra | None = None,
|
34
38
|
) -> TDataclass:
|
35
39
|
"""Load a set of settings from the `.env` file."""
|
36
|
-
path =
|
40
|
+
path = get_root(path=path).joinpath(".env")
|
37
41
|
if not path.exists():
|
38
42
|
raise _LoadSettingsFileNotFoundError(path=path) from None
|
39
43
|
maybe_values_dotenv = dotenv_values(path)
|
utilities/types.py
CHANGED
@@ -241,8 +241,8 @@ type SerializeObjectExtra = Mapping[Any, Callable[[Any], str]]
|
|
241
241
|
|
242
242
|
|
243
243
|
# pathlib
|
244
|
+
type MaybeCallablePathLike = MaybeCallable[PathLike]
|
244
245
|
type PathLike = MaybeStr[Path]
|
245
|
-
type PathLikeOrCallable = PathLike | Callable[[], PathLike]
|
246
246
|
|
247
247
|
|
248
248
|
# random
|
@@ -282,6 +282,7 @@ __all__ = [
|
|
282
282
|
"MaybeCallableDate",
|
283
283
|
"MaybeCallableDateTime",
|
284
284
|
"MaybeCallableEvent",
|
285
|
+
"MaybeCallablePathLike",
|
285
286
|
"MaybeCoroutine1",
|
286
287
|
"MaybeIterable",
|
287
288
|
"MaybeIterableHashable",
|
@@ -293,7 +294,6 @@ __all__ = [
|
|
293
294
|
"Parallelism",
|
294
295
|
"ParseObjectExtra",
|
295
296
|
"PathLike",
|
296
|
-
"PathLikeOrCallable",
|
297
297
|
"RoundMode",
|
298
298
|
"Seed",
|
299
299
|
"SerializeObjectExtra",
|
utilities/version.py
CHANGED
@@ -137,14 +137,6 @@ def get_version(*, version: MaybeCallableVersionLike) -> Version: ...
|
|
137
137
|
def get_version(*, version: None) -> None: ...
|
138
138
|
@overload
|
139
139
|
def get_version(*, version: Sentinel) -> Sentinel: ...
|
140
|
-
@overload
|
141
|
-
def get_version(
|
142
|
-
*, version: MaybeCallableVersionLike | Sentinel
|
143
|
-
) -> Version | Sentinel: ...
|
144
|
-
@overload
|
145
|
-
def get_version(
|
146
|
-
*, version: MaybeCallableVersionLike | None | Sentinel = sentinel
|
147
|
-
) -> Version | None | Sentinel: ...
|
148
140
|
def get_version(
|
149
141
|
*, version: MaybeCallableVersionLike | None | Sentinel = sentinel
|
150
142
|
) -> Version | None | Sentinel:
|
utilities/git.py
DELETED
@@ -1,93 +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_call, 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 fetch_all_tags(*, cwd: PathLike = PWD) -> None:
|
16
|
-
"""Fetch the tags."""
|
17
|
-
_ = check_call(["git", "fetch", "--all", "--tags"], cwd=cwd)
|
18
|
-
|
19
|
-
|
20
|
-
##
|
21
|
-
|
22
|
-
|
23
|
-
def get_branch_name(*, cwd: PathLike = PWD) -> str:
|
24
|
-
"""Get the current branch name."""
|
25
|
-
output = check_output(
|
26
|
-
_GIT_REV_PARSE_ABBREV_REV_HEAD, stderr=PIPE, cwd=cwd, text=True
|
27
|
-
)
|
28
|
-
return output.strip("\n")
|
29
|
-
|
30
|
-
|
31
|
-
_GIT_REV_PARSE_ABBREV_REV_HEAD = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
|
32
|
-
|
33
|
-
|
34
|
-
##
|
35
|
-
|
36
|
-
|
37
|
-
def get_ref_tags(ref: str, /, *, cwd: PathLike = PWD) -> list[str]:
|
38
|
-
"""Get the tags of a reference."""
|
39
|
-
output = check_output([*_GIT_TAG_POINTS_AT, ref], stderr=PIPE, cwd=cwd, text=True)
|
40
|
-
return output.strip("\n").splitlines()
|
41
|
-
|
42
|
-
|
43
|
-
_GIT_TAG_POINTS_AT = ["git", "tag", "--points-at"]
|
44
|
-
|
45
|
-
|
46
|
-
##
|
47
|
-
|
48
|
-
|
49
|
-
def get_repo_name(*, cwd: PathLike = PWD) -> str:
|
50
|
-
"""Get the repo name."""
|
51
|
-
output = check_output(_GIT_REMOTE_GET_URL_ORIGIN, stderr=PIPE, cwd=cwd, text=True)
|
52
|
-
return Path(output.strip("\n")).stem # not valid_path
|
53
|
-
|
54
|
-
|
55
|
-
_GIT_REMOTE_GET_URL_ORIGIN = ["git", "remote", "get-url", "origin"]
|
56
|
-
|
57
|
-
|
58
|
-
##
|
59
|
-
|
60
|
-
|
61
|
-
def get_repo_root(*, cwd: PathLike = PWD) -> Path:
|
62
|
-
"""Get the repo root."""
|
63
|
-
try:
|
64
|
-
output = check_output(
|
65
|
-
["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=cwd, text=True
|
66
|
-
)
|
67
|
-
except CalledProcessError as error:
|
68
|
-
# newer versions of git report "Not a git repository", whilst older
|
69
|
-
# versions report "not a git repository"
|
70
|
-
if search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
|
71
|
-
raise GetRepoRootError(cwd=cwd) from error
|
72
|
-
raise # pragma: no cover
|
73
|
-
else:
|
74
|
-
return Path(output.strip("\n"))
|
75
|
-
|
76
|
-
|
77
|
-
@dataclass(kw_only=True, slots=True)
|
78
|
-
class GetRepoRootError(Exception):
|
79
|
-
cwd: PathLike
|
80
|
-
|
81
|
-
@override
|
82
|
-
def __str__(self) -> str:
|
83
|
-
return f"Path is not part of a `git` repository: {self.cwd}"
|
84
|
-
|
85
|
-
|
86
|
-
__all__ = [
|
87
|
-
"GetRepoRootError",
|
88
|
-
"fetch_all_tags",
|
89
|
-
"get_branch_name",
|
90
|
-
"get_ref_tags",
|
91
|
-
"get_repo_name",
|
92
|
-
"get_repo_root",
|
93
|
-
]
|
File without changes
|
File without changes
|