dycw-utilities 0.174.5__py3-none-any.whl → 0.174.6__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.174.5.dist-info → dycw_utilities-0.174.6.dist-info}/METADATA +1 -1
- {dycw_utilities-0.174.5.dist-info → dycw_utilities-0.174.6.dist-info}/RECORD +6 -6
- utilities/__init__.py +1 -1
- utilities/subprocess.py +106 -8
- {dycw_utilities-0.174.5.dist-info → dycw_utilities-0.174.6.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.174.5.dist-info → dycw_utilities-0.174.6.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
utilities/__init__.py,sha256=
|
|
1
|
+
utilities/__init__.py,sha256=3pVEXocwWUcnyWJHWq_I_o96RALb2ltauJoGiOsfki8,60
|
|
2
2
|
utilities/aeventkit.py,sha256=OmDBhYGgbsKrB7cdC5FFpJHUatX9O76eTeKVVTksp2Y,12673
|
|
3
3
|
utilities/altair.py,sha256=rUK99g9x6CYDDfiZrf-aTx5fSRbL1Q8ctgKORowzXHg,9060
|
|
4
4
|
utilities/asyncio.py,sha256=aJySVxBY0gqsIYnoNmH7-1r8djKuf4vSsU69VCD08t8,16772
|
|
@@ -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=qrPDBTVKy_RMTnNFD4mQ7iYlNncXQiDBGJhffh6K0uc,18216
|
|
84
84
|
utilities/tempfile.py,sha256=Lx6qa16lL1XVH6WdmD_G9vlN6gLI8nrIurxmsFkPKvg,3022
|
|
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.174.
|
|
101
|
-
dycw_utilities-0.174.
|
|
102
|
-
dycw_utilities-0.174.
|
|
103
|
-
dycw_utilities-0.174.
|
|
100
|
+
dycw_utilities-0.174.6.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
|
|
101
|
+
dycw_utilities-0.174.6.dist-info/entry_points.txt,sha256=ykGI1ArwOPHqm2g5Cqh3ENdMxEej_a_FcOUov5EM5Oc,155
|
|
102
|
+
dycw_utilities-0.174.6.dist-info/METADATA,sha256=9UHGH9P99oGq0OZI0ERbOeM_X2x3M2EO6C3-xo16np4,1709
|
|
103
|
+
dycw_utilities-0.174.6.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/subprocess.py
CHANGED
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
from contextlib import contextmanager
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from io import StringIO
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from string import Template
|
|
8
9
|
from subprocess import PIPE, CalledProcessError, Popen
|
|
9
10
|
from threading import Thread
|
|
10
11
|
from time import sleep
|
|
11
|
-
from typing import IO, TYPE_CHECKING, Literal, assert_never, overload
|
|
12
|
+
from typing import IO, TYPE_CHECKING, Literal, assert_never, overload, override
|
|
12
13
|
|
|
13
14
|
from utilities.errors import ImpossibleCaseError
|
|
14
15
|
from utilities.logging import to_logger
|
|
@@ -22,9 +23,52 @@ if TYPE_CHECKING:
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
_HOST_KEY_ALGORITHMS = ["ssh-ed25519"]
|
|
26
|
+
APT_UPDATE = ["apt", "update", "-y"]
|
|
25
27
|
BASH_LC = ["bash", "-lc"]
|
|
26
28
|
BASH_LS = ["bash", "-ls"]
|
|
27
29
|
MKTEMP_DIR_CMD = ["mktemp", "-d"]
|
|
30
|
+
RESTART_SSHD = ["systemctl", "restart", "sshd"]
|
|
31
|
+
UPDATE_CA_CERTIFICATES: str = "update-ca-certificates"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def apt_install_cmd(package: str, /) -> list[str]:
|
|
35
|
+
return ["apt", "install", "-y", package]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def cat_cmd(path: PathLike, /) -> list[str]:
|
|
39
|
+
return ["cat", str(path)]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def cd_cmd(path: PathLike, /) -> list[str]:
|
|
43
|
+
return ["cd", str(path)]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def chmod_cmd(path: PathLike, mode: str, /) -> list[str]:
|
|
47
|
+
return ["chmod", mode, str(path)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def chown_cmd(
|
|
51
|
+
path: PathLike, /, *, user: str | None = None, group: str | None = None
|
|
52
|
+
) -> list[str]:
|
|
53
|
+
match user, group:
|
|
54
|
+
case None, None:
|
|
55
|
+
raise ChownCmdError
|
|
56
|
+
case str(), None:
|
|
57
|
+
ownership = "user"
|
|
58
|
+
case None, str():
|
|
59
|
+
ownership = f":{group}"
|
|
60
|
+
case str(), str():
|
|
61
|
+
ownership = f"{user}:{group}"
|
|
62
|
+
case never:
|
|
63
|
+
assert_never(never)
|
|
64
|
+
return ["chown", ownership, str(path)]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(kw_only=True, slots=True)
|
|
68
|
+
class ChownCmdError(Exception):
|
|
69
|
+
@override
|
|
70
|
+
def __str__(self) -> str:
|
|
71
|
+
return "At least one of 'user' and/or 'group' must be given; got None"
|
|
28
72
|
|
|
29
73
|
|
|
30
74
|
def cp_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
|
|
@@ -45,6 +89,15 @@ def expand_path(
|
|
|
45
89
|
return Path(path).expanduser()
|
|
46
90
|
|
|
47
91
|
|
|
92
|
+
def git_clone_cmd(url: str, path: PathLike, /) -> list[str]:
|
|
93
|
+
return ["git", "clone", "--recurse-submodules", url, str(path)]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def git_hard_reset_cmd(*, branch: str | None = None) -> list[str]:
|
|
97
|
+
branch_use = "master" if branch is None else branch
|
|
98
|
+
return ["git", "hard-reset", branch_use]
|
|
99
|
+
|
|
100
|
+
|
|
48
101
|
def maybe_sudo_cmd(cmd: str, /, *args: str, sudo: bool = False) -> list[str]:
|
|
49
102
|
parts: list[str] = [cmd, *args]
|
|
50
103
|
return sudo_cmd(*parts) if sudo else parts
|
|
@@ -224,8 +277,8 @@ def run(
|
|
|
224
277
|
if proc.stderr is None: # pragma: no cover
|
|
225
278
|
raise ImpossibleCaseError(case=[f"{proc.stderr=}"])
|
|
226
279
|
with (
|
|
227
|
-
|
|
228
|
-
|
|
280
|
+
_run_yield_write(proc.stdout, *stdout_outputs),
|
|
281
|
+
_run_yield_write(proc.stderr, *stderr_outputs),
|
|
229
282
|
):
|
|
230
283
|
if input is not None:
|
|
231
284
|
_ = proc.stdin.write(input)
|
|
@@ -307,8 +360,8 @@ def run(
|
|
|
307
360
|
|
|
308
361
|
|
|
309
362
|
@contextmanager
|
|
310
|
-
def
|
|
311
|
-
thread = Thread(target=
|
|
363
|
+
def _run_yield_write(input_: IO[str], /, *outputs: IO[str]) -> Iterator[None]:
|
|
364
|
+
thread = Thread(target=_run_daemon_target, args=(input_, *outputs), daemon=True)
|
|
312
365
|
thread.start()
|
|
313
366
|
try:
|
|
314
367
|
yield
|
|
@@ -316,17 +369,21 @@ def _yield_write(input_: IO[str], /, *outputs: IO[str]) -> Iterator[None]:
|
|
|
316
369
|
thread.join()
|
|
317
370
|
|
|
318
371
|
|
|
319
|
-
def
|
|
372
|
+
def _run_daemon_target(input_: IO[str], /, *outputs: IO[str]) -> None:
|
|
320
373
|
with input_:
|
|
321
374
|
for text in iter(input_.readline, ""):
|
|
322
|
-
|
|
375
|
+
_run_write_to_streams(text, *outputs)
|
|
323
376
|
|
|
324
377
|
|
|
325
|
-
def
|
|
378
|
+
def _run_write_to_streams(text: str, /, *outputs: IO[str]) -> None:
|
|
326
379
|
for output in outputs:
|
|
327
380
|
_ = output.write(text)
|
|
328
381
|
|
|
329
382
|
|
|
383
|
+
def set_hostname_cmd(hostname: str, /) -> list[str]:
|
|
384
|
+
return ["hostnamectl", "set-hostname", hostname]
|
|
385
|
+
|
|
386
|
+
|
|
330
387
|
@overload
|
|
331
388
|
def ssh(
|
|
332
389
|
user: str,
|
|
@@ -480,14 +537,41 @@ def ssh_cmd(
|
|
|
480
537
|
return [*args, "-T", f"{user}@{hostname}", *cmd_and_cmds_or_args]
|
|
481
538
|
|
|
482
539
|
|
|
540
|
+
def ssh_keygen_cmd(hostname: str, /) -> list[str]:
|
|
541
|
+
return ["ssh-keygen", "-f", "~/.ssh/known_hosts", "-R", hostname]
|
|
542
|
+
|
|
543
|
+
|
|
483
544
|
def sudo_cmd(cmd: str, /, *args: str) -> list[str]:
|
|
484
545
|
return ["sudo", cmd, *args]
|
|
485
546
|
|
|
486
547
|
|
|
548
|
+
def sudo_nopasswd_cmd(user: str, /) -> str:
|
|
549
|
+
return f"{user} ALL=(ALL) NOPASSWD: ALL"
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def symlink_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
|
|
553
|
+
return ["ln", "-s", str(src), str(dest)]
|
|
554
|
+
|
|
555
|
+
|
|
487
556
|
def touch_cmd(path: PathLike, /) -> list[str]:
|
|
488
557
|
return ["touch", str(path)]
|
|
489
558
|
|
|
490
559
|
|
|
560
|
+
def uv_run_cmd(module: str, /, *args: str) -> list[str]:
|
|
561
|
+
return [
|
|
562
|
+
"uv",
|
|
563
|
+
"run",
|
|
564
|
+
"--no-dev",
|
|
565
|
+
"--active",
|
|
566
|
+
"--prerelease=disallow",
|
|
567
|
+
"--managed-python",
|
|
568
|
+
"python",
|
|
569
|
+
"-m",
|
|
570
|
+
module,
|
|
571
|
+
*args,
|
|
572
|
+
]
|
|
573
|
+
|
|
574
|
+
|
|
491
575
|
@contextmanager
|
|
492
576
|
def yield_ssh_temp_dir(
|
|
493
577
|
user: str,
|
|
@@ -512,21 +596,35 @@ def yield_ssh_temp_dir(
|
|
|
512
596
|
|
|
513
597
|
|
|
514
598
|
__all__ = [
|
|
599
|
+
"APT_UPDATE",
|
|
515
600
|
"BASH_LC",
|
|
516
601
|
"BASH_LS",
|
|
517
602
|
"MKTEMP_DIR_CMD",
|
|
603
|
+
"RESTART_SSHD",
|
|
604
|
+
"UPDATE_CA_CERTIFICATES",
|
|
605
|
+
"ChownCmdError",
|
|
606
|
+
"apt_install_cmd",
|
|
607
|
+
"cd_cmd",
|
|
608
|
+
"chmod_cmd",
|
|
609
|
+
"chown_cmd",
|
|
518
610
|
"cp_cmd",
|
|
519
611
|
"echo_cmd",
|
|
520
612
|
"expand_path",
|
|
613
|
+
"git_clone_cmd",
|
|
614
|
+
"git_hard_reset_cmd",
|
|
521
615
|
"maybe_sudo_cmd",
|
|
522
616
|
"mkdir",
|
|
523
617
|
"mkdir_cmd",
|
|
524
618
|
"mv_cmd",
|
|
525
619
|
"rm_cmd",
|
|
526
620
|
"run",
|
|
621
|
+
"set_hostname_cmd",
|
|
527
622
|
"ssh",
|
|
528
623
|
"ssh_cmd",
|
|
529
624
|
"sudo_cmd",
|
|
625
|
+
"sudo_nopasswd_cmd",
|
|
626
|
+
"symlink_cmd",
|
|
530
627
|
"touch_cmd",
|
|
628
|
+
"uv_run_cmd",
|
|
531
629
|
"yield_ssh_temp_dir",
|
|
532
630
|
]
|
|
File without changes
|
|
File without changes
|