dycw-utilities 0.175.28__py3-none-any.whl → 0.175.30__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.28.dist-info → dycw_utilities-0.175.30.dist-info}/METADATA +1 -1
- {dycw_utilities-0.175.28.dist-info → dycw_utilities-0.175.30.dist-info}/RECORD +7 -7
- utilities/__init__.py +1 -1
- utilities/docker.py +88 -27
- utilities/subprocess.py +35 -9
- {dycw_utilities-0.175.28.dist-info → dycw_utilities-0.175.30.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.175.28.dist-info → dycw_utilities-0.175.30.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
utilities/__init__.py,sha256=
|
|
1
|
+
utilities/__init__.py,sha256=MtsJJrbSJrW256hO7llrT3qk9914hd3MBaFYuonK4KU,61
|
|
2
2
|
utilities/altair.py,sha256=TLfRFbG9HwG7SLXoJ-v0r-t49ZaGgTQZD82cpjVi4vs,9085
|
|
3
3
|
utilities/asyncio.py,sha256=aJySVxBY0gqsIYnoNmH7-1r8djKuf4vSsU69VCD08t8,16772
|
|
4
4
|
utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
|
|
@@ -11,7 +11,7 @@ utilities/contextvars.py,sha256=J8OhC7jqozAGYOCe2KUWysbPXNGe5JYz3HfaY_mIs08,883
|
|
|
11
11
|
utilities/cryptography.py,sha256=5PFrzsNUGHay91dFgYnDKwYprXxahrBqztmUqViRzBk,956
|
|
12
12
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
|
13
13
|
utilities/dataclasses.py,sha256=xbU3QN1GFy7RC6hIJRZIeUZm7YRlodrgEWmahWG6k2g,32465
|
|
14
|
-
utilities/docker.py,sha256=
|
|
14
|
+
utilities/docker.py,sha256=nzeuR5-OQKL2yhtY-SSqLS2ftl-iev0cUrshwEMwB2k,9808
|
|
15
15
|
utilities/enum.py,sha256=5l6pwZD1cjSlVW4ss-zBPspWvrbrYrdtJWcg6f5_J5w,5781
|
|
16
16
|
utilities/errors.py,sha256=mFlDGSM0LI1jZ1pbqwLAH3ttLZ2JVIxyZLojw8tGVZU,1479
|
|
17
17
|
utilities/fastapi.py,sha256=TqyKvBjiMS594sXPjrz-KRTLMb3l3D3rZ1zAYV7GfOk,1454
|
|
@@ -80,7 +80,7 @@ utilities/sqlalchemy.py,sha256=HQYpd7LFxdTF5WYVWYtCJeEBI71EJm7ytvCGyAH9B-U,37163
|
|
|
80
80
|
utilities/sqlalchemy_polars.py,sha256=JCGhB37raSR7fqeWV5dTsciRTMVzIdVT9YSqKT0piT0,13370
|
|
81
81
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
|
82
82
|
utilities/string.py,sha256=shmBK87zZwzGyixuNuXCiUbqzfeZ9xlrFwz6JTaRvDk,582
|
|
83
|
-
utilities/subprocess.py,sha256=
|
|
83
|
+
utilities/subprocess.py,sha256=ei0OD73S0WX7ZqHCQp9UB3hY9OWbAUVIrLoz9niIfQQ,53031
|
|
84
84
|
utilities/tempfile.py,sha256=a3_M1QyxGZql_VcGkBOQBeWbbkItjgkfIpVyzU1UAic,3843
|
|
85
85
|
utilities/testbook.py,sha256=j1KmaVbrX9VrbeMgtPh5gk55myAsn3dyRUn7jGbPbRk,1294
|
|
86
86
|
utilities/text.py,sha256=7SvwcSR2l_5cOrm1samGnR4C-ZI6qyFLHLzSpO1zeHQ,13958
|
|
@@ -97,7 +97,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
|
97
97
|
utilities/whenever.py,sha256=F4ek0-OBWxHYrZdmoZt76N2RnNyKY5KrEHt7rqO4AQE,60183
|
|
98
98
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
|
99
99
|
utilities/zoneinfo.py,sha256=tdIScrTB2-B-LH0ukb1HUXKooLknOfJNwHk10MuMYvA,3619
|
|
100
|
-
dycw_utilities-0.175.
|
|
101
|
-
dycw_utilities-0.175.
|
|
102
|
-
dycw_utilities-0.175.
|
|
103
|
-
dycw_utilities-0.175.
|
|
100
|
+
dycw_utilities-0.175.30.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
101
|
+
dycw_utilities-0.175.30.dist-info/entry_points.txt,sha256=cOGtKeJI0KXLSV7MJ8Dhc2G8jPgDcBDm53MVNJU4ycI,136
|
|
102
|
+
dycw_utilities-0.175.30.dist-info/METADATA,sha256=X2Hiaa6YhG9ZkFu1AfxlEDbXKDKYepqcVZylTh5hVds,1443
|
|
103
|
+
dycw_utilities-0.175.30.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/docker.py
CHANGED
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from typing import TYPE_CHECKING, Literal, overload
|
|
6
6
|
|
|
7
7
|
from utilities.errors import ImpossibleCaseError
|
|
8
|
+
from utilities.iterables import always_iterable
|
|
8
9
|
from utilities.logging import to_logger
|
|
9
10
|
from utilities.subprocess import (
|
|
10
11
|
MKTEMP_DIR_CMD,
|
|
@@ -18,7 +19,60 @@ from utilities.subprocess import (
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from collections.abc import Iterator
|
|
20
21
|
|
|
21
|
-
from utilities.types import
|
|
22
|
+
from utilities.types import (
|
|
23
|
+
LoggerLike,
|
|
24
|
+
MaybeIterable,
|
|
25
|
+
PathLike,
|
|
26
|
+
Retry,
|
|
27
|
+
StrStrMapping,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def docker_compose_down(*, files: MaybeIterable[PathLike] | None = None) -> None:
|
|
32
|
+
"""Stop and remove containers."""
|
|
33
|
+
run(*docker_compose_down_cmd(files=files)) # pragma: no cover
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def docker_compose_down_cmd(
|
|
37
|
+
*, files: MaybeIterable[PathLike] | None = None
|
|
38
|
+
) -> list[str]:
|
|
39
|
+
"""Command to use 'docker compose down' to stop and remove containers."""
|
|
40
|
+
return _docker_compose_cmd("down", files=files)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def docker_compose_pull(*, files: MaybeIterable[PathLike] | None = None) -> None:
|
|
44
|
+
"""Pull service images."""
|
|
45
|
+
run(*docker_compose_pull_cmd(files=files)) # pragma: no cover
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def docker_compose_pull_cmd(
|
|
49
|
+
*, files: MaybeIterable[PathLike] | None = None
|
|
50
|
+
) -> list[str]:
|
|
51
|
+
"""Command to use 'docker compose pull' to pull service images."""
|
|
52
|
+
return _docker_compose_cmd("pull", files=files)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def docker_compose_up(*, files: MaybeIterable[PathLike] | None = None) -> None:
|
|
56
|
+
"""Create and start containers."""
|
|
57
|
+
run(*docker_compose_up_cmd(files=files)) # pragma: no cover
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def docker_compose_up_cmd(*, files: MaybeIterable[PathLike] | None = None) -> list[str]:
|
|
61
|
+
"""Command to use 'docker compose up' to create and start containers."""
|
|
62
|
+
return _docker_compose_cmd("up", files=files)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _docker_compose_cmd(
|
|
66
|
+
cmd: str, /, *, files: MaybeIterable[PathLike] | None = None
|
|
67
|
+
) -> list[str]:
|
|
68
|
+
args: list[str] = ["docker", "compose"]
|
|
69
|
+
if files is not None:
|
|
70
|
+
for file in always_iterable(files):
|
|
71
|
+
args.extend(["--file", str(file)])
|
|
72
|
+
return [*args, cmd]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
##
|
|
22
76
|
|
|
23
77
|
|
|
24
78
|
@overload
|
|
@@ -47,48 +101,39 @@ def docker_cp(
|
|
|
47
101
|
sudo: bool = False,
|
|
48
102
|
logger: LoggerLike | None = None,
|
|
49
103
|
) -> None:
|
|
104
|
+
"""Copy between a container and the local file system."""
|
|
50
105
|
match src, dest: # skipif-ci
|
|
51
106
|
case Path() | str(), (str() as cont, Path() | str() as dest_path):
|
|
52
107
|
docker_exec(
|
|
53
108
|
cont, *maybe_sudo_cmd(*mkdir_cmd(dest_path, parent=True), sudo=sudo)
|
|
54
109
|
)
|
|
55
|
-
run(*docker_cp_cmd(src, dest, sudo=sudo), logger=logger)
|
|
110
|
+
run(*maybe_sudo_cmd(*docker_cp_cmd(src, dest), sudo=sudo), logger=logger)
|
|
56
111
|
case (str(), Path() | str()), Path() | str():
|
|
57
112
|
mkdir(dest, parent=True, sudo=sudo)
|
|
58
|
-
run(*docker_cp_cmd(src, dest, sudo=sudo), logger=logger)
|
|
113
|
+
run(*maybe_sudo_cmd(*docker_cp_cmd(src, dest), sudo=sudo), logger=logger)
|
|
59
114
|
case _: # pragma: no cover
|
|
60
115
|
raise ImpossibleCaseError(case=[f"{src}", f"{dest=}"])
|
|
61
116
|
|
|
62
117
|
|
|
63
118
|
@overload
|
|
64
|
-
def docker_cp_cmd(
|
|
65
|
-
src: tuple[str, PathLike], dest: PathLike, /, *, sudo: bool = False
|
|
66
|
-
) -> list[str]: ...
|
|
119
|
+
def docker_cp_cmd(src: tuple[str, PathLike], dest: PathLike, /) -> list[str]: ...
|
|
67
120
|
@overload
|
|
121
|
+
def docker_cp_cmd(src: PathLike, dest: tuple[str, PathLike], /) -> list[str]: ...
|
|
68
122
|
def docker_cp_cmd(
|
|
69
|
-
src: PathLike
|
|
70
|
-
) -> list[str]: ...
|
|
71
|
-
def docker_cp_cmd(
|
|
72
|
-
src: PathLike | tuple[str, PathLike],
|
|
73
|
-
dest: PathLike | tuple[str, PathLike],
|
|
74
|
-
/,
|
|
75
|
-
*,
|
|
76
|
-
sudo: bool = False,
|
|
123
|
+
src: PathLike | tuple[str, PathLike], dest: PathLike | tuple[str, PathLike], /
|
|
77
124
|
) -> list[str]:
|
|
125
|
+
"""Command to use 'docker cp' to copy between a container and the local file system."""
|
|
126
|
+
args: list[str] = ["docker", "cp"]
|
|
78
127
|
match src, dest:
|
|
79
|
-
case (Path() | str()) as
|
|
80
|
-
str()
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
dest_use = f"{dest_cont}:{dest_path}"
|
|
84
|
-
case (str() as src_cont, (Path() | str()) as src_path), (
|
|
85
|
-
Path() | str() as dest_use
|
|
86
|
-
):
|
|
87
|
-
src_use = f"{src_cont}:{src_path}"
|
|
128
|
+
case ((Path() | str()), (str() as cont, Path() | str() as path)):
|
|
129
|
+
return [*args, str(src), f"{cont}:{path}"]
|
|
130
|
+
case (str() as cont, (Path() | str()) as path), (Path() | str() as dest):
|
|
131
|
+
return [*args, f"{cont}:{path}", str(dest)]
|
|
88
132
|
case _: # pragma: no cover
|
|
89
133
|
raise ImpossibleCaseError(case=[f"{src}", f"{dest=}"])
|
|
90
|
-
|
|
91
|
-
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
##
|
|
92
137
|
|
|
93
138
|
|
|
94
139
|
@overload
|
|
@@ -210,6 +255,7 @@ def docker_exec(
|
|
|
210
255
|
logger: LoggerLike | None = None,
|
|
211
256
|
**env_kwargs: str,
|
|
212
257
|
) -> str | None:
|
|
258
|
+
"""Execute a command in a container."""
|
|
213
259
|
cmd_and_args = docker_exec_cmd( # skipif-ci
|
|
214
260
|
container,
|
|
215
261
|
cmd,
|
|
@@ -245,7 +291,7 @@ def docker_exec_cmd(
|
|
|
245
291
|
workdir: PathLike | None = None,
|
|
246
292
|
**env_kwargs: str,
|
|
247
293
|
) -> list[str]:
|
|
248
|
-
"""
|
|
294
|
+
"""Command to use `docker exec` to execute a command in a container."""
|
|
249
295
|
args: list[str] = ["docker", "exec"]
|
|
250
296
|
mapping: dict[str, str] = ({} if env is None else dict(env)) | env_kwargs
|
|
251
297
|
for key, value in mapping.items():
|
|
@@ -259,6 +305,9 @@ def docker_exec_cmd(
|
|
|
259
305
|
return [*args, container, cmd, *cmds_or_args]
|
|
260
306
|
|
|
261
307
|
|
|
308
|
+
##
|
|
309
|
+
|
|
310
|
+
|
|
262
311
|
@contextmanager
|
|
263
312
|
def yield_docker_temp_dir(
|
|
264
313
|
container: str,
|
|
@@ -290,4 +339,16 @@ def yield_docker_temp_dir(
|
|
|
290
339
|
docker_exec(container, *rm_cmd(path), user=user, retry=retry, logger=logger)
|
|
291
340
|
|
|
292
341
|
|
|
293
|
-
__all__ = [
|
|
342
|
+
__all__ = [
|
|
343
|
+
"docker_compose_down",
|
|
344
|
+
"docker_compose_down_cmd",
|
|
345
|
+
"docker_compose_pull",
|
|
346
|
+
"docker_compose_pull_cmd",
|
|
347
|
+
"docker_compose_up",
|
|
348
|
+
"docker_compose_up_cmd",
|
|
349
|
+
"docker_cp",
|
|
350
|
+
"docker_cp_cmd",
|
|
351
|
+
"docker_exec",
|
|
352
|
+
"docker_exec_cmd",
|
|
353
|
+
"yield_docker_temp_dir",
|
|
354
|
+
]
|
utilities/subprocess.py
CHANGED
|
@@ -86,9 +86,10 @@ def apt_install(
|
|
|
86
86
|
"""Install packages."""
|
|
87
87
|
if update: # pragma: no cover
|
|
88
88
|
apt_update(sudo=sudo)
|
|
89
|
-
|
|
90
|
-
*
|
|
89
|
+
args = maybe_sudo_cmd( # pragma: no cover
|
|
90
|
+
*apt_install_cmd(package, *packages), sudo=sudo
|
|
91
91
|
)
|
|
92
|
+
run(*args) # pragma: no cover
|
|
92
93
|
|
|
93
94
|
|
|
94
95
|
def apt_install_cmd(package: str, /, *packages: str) -> list[str]:
|
|
@@ -101,9 +102,10 @@ def apt_install_cmd(package: str, /, *packages: str) -> list[str]:
|
|
|
101
102
|
|
|
102
103
|
def apt_remove(package: str, /, *packages: str, sudo: bool = False) -> None:
|
|
103
104
|
"""Remove a package."""
|
|
104
|
-
|
|
105
|
-
*
|
|
105
|
+
args = maybe_sudo_cmd( # pragma: no cover
|
|
106
|
+
*apt_remove_cmd(package, *packages), sudo=sudo
|
|
106
107
|
)
|
|
108
|
+
run(*args) # pragma: no cover
|
|
107
109
|
|
|
108
110
|
|
|
109
111
|
def apt_remove_cmd(package: str, /, *packages: str) -> list[str]:
|
|
@@ -146,6 +148,29 @@ def cd_cmd(path: PathLike, /) -> list[str]:
|
|
|
146
148
|
##
|
|
147
149
|
|
|
148
150
|
|
|
151
|
+
def chattr(
|
|
152
|
+
path: PathLike, /, *, immutable: bool | None = None, sudo: bool = False
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Change file attributes."""
|
|
155
|
+
args = maybe_sudo_cmd( # pragma: no cover
|
|
156
|
+
*chattr_cmd(path, immutable=immutable), sudo=sudo
|
|
157
|
+
)
|
|
158
|
+
run(*args) # pragma: no cover
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def chattr_cmd(path: PathLike, /, *, immutable: bool | None = None) -> list[str]:
|
|
162
|
+
"""Command to use 'chattr' to change file attributes."""
|
|
163
|
+
args: list[str] = ["chattr"]
|
|
164
|
+
if immutable is True:
|
|
165
|
+
args.append("+i")
|
|
166
|
+
elif immutable is False:
|
|
167
|
+
args.append("-i")
|
|
168
|
+
return [*args, str(path)]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
##
|
|
172
|
+
|
|
173
|
+
|
|
149
174
|
def chmod(path: PathLike, perms: PermissionsLike, /, *, sudo: bool = False) -> None:
|
|
150
175
|
"""Change file mode."""
|
|
151
176
|
if sudo: # pragma: no cover
|
|
@@ -223,9 +248,8 @@ class ChownCmdError(Exception):
|
|
|
223
248
|
|
|
224
249
|
def chpasswd(user_name: str, password: str, /, *, sudo: bool = False) -> None:
|
|
225
250
|
"""Update passwords."""
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
251
|
+
args = maybe_sudo_cmd(CHPASSWD, sudo=sudo) # pragma: no cover
|
|
252
|
+
run(*args, input=f"{user_name}:{password}") # pragma: no cover
|
|
229
253
|
|
|
230
254
|
|
|
231
255
|
##
|
|
@@ -421,8 +445,8 @@ def curl(
|
|
|
421
445
|
logger: LoggerLike | None = None,
|
|
422
446
|
) -> str | None:
|
|
423
447
|
"""Transfer a URL."""
|
|
424
|
-
args = maybe_sudo_cmd(
|
|
425
|
-
*curl_cmd(
|
|
448
|
+
args = maybe_sudo_cmd( # skipif-ci
|
|
449
|
+
*curl_cmd(
|
|
426
450
|
url,
|
|
427
451
|
fail=fail,
|
|
428
452
|
location=location,
|
|
@@ -1860,6 +1884,8 @@ __all__ = [
|
|
|
1860
1884
|
"apt_update",
|
|
1861
1885
|
"cat",
|
|
1862
1886
|
"cd_cmd",
|
|
1887
|
+
"chattr",
|
|
1888
|
+
"chattr_cmd",
|
|
1863
1889
|
"chmod",
|
|
1864
1890
|
"chmod_cmd",
|
|
1865
1891
|
"chown",
|
|
File without changes
|
|
File without changes
|