dycw-utilities 0.174.16__py3-none-any.whl → 0.174.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.
- {dycw_utilities-0.174.16.dist-info → dycw_utilities-0.174.17.dist-info}/METADATA +1 -1
- {dycw_utilities-0.174.16.dist-info → dycw_utilities-0.174.17.dist-info}/RECORD +6 -6
- {dycw_utilities-0.174.16.dist-info → dycw_utilities-0.174.17.dist-info}/WHEEL +1 -1
- utilities/__init__.py +1 -1
- utilities/subprocess.py +144 -12
- {dycw_utilities-0.174.16.dist-info → dycw_utilities-0.174.17.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
utilities/__init__.py,sha256=
|
|
1
|
+
utilities/__init__.py,sha256=kcADuh_nb8xfuYrat4AkfaKqdxp3l7xyrXWoJJ5JZLk,61
|
|
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
|
|
@@ -81,7 +81,7 @@ utilities/sqlalchemy.py,sha256=HQYpd7LFxdTF5WYVWYtCJeEBI71EJm7ytvCGyAH9B-U,37163
|
|
|
81
81
|
utilities/sqlalchemy_polars.py,sha256=JCGhB37raSR7fqeWV5dTsciRTMVzIdVT9YSqKT0piT0,13370
|
|
82
82
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
|
83
83
|
utilities/string.py,sha256=shmBK87zZwzGyixuNuXCiUbqzfeZ9xlrFwz6JTaRvDk,582
|
|
84
|
-
utilities/subprocess.py,sha256=
|
|
84
|
+
utilities/subprocess.py,sha256=FzVd1evvhaJR2U_tATJ37Y6YjF8O61XPeqYo8NLonG4,33123
|
|
85
85
|
utilities/tempfile.py,sha256=Lx6qa16lL1XVH6WdmD_G9vlN6gLI8nrIurxmsFkPKvg,3022
|
|
86
86
|
utilities/testbook.py,sha256=j1KmaVbrX9VrbeMgtPh5gk55myAsn3dyRUn7jGbPbRk,1294
|
|
87
87
|
utilities/text.py,sha256=7SvwcSR2l_5cOrm1samGnR4C-ZI6qyFLHLzSpO1zeHQ,13958
|
|
@@ -98,7 +98,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
|
98
98
|
utilities/whenever.py,sha256=F4ek0-OBWxHYrZdmoZt76N2RnNyKY5KrEHt7rqO4AQE,60183
|
|
99
99
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
|
100
100
|
utilities/zoneinfo.py,sha256=tdIScrTB2-B-LH0ukb1HUXKooLknOfJNwHk10MuMYvA,3619
|
|
101
|
-
dycw_utilities-0.174.
|
|
102
|
-
dycw_utilities-0.174.
|
|
103
|
-
dycw_utilities-0.174.
|
|
104
|
-
dycw_utilities-0.174.
|
|
101
|
+
dycw_utilities-0.174.17.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
102
|
+
dycw_utilities-0.174.17.dist-info/entry_points.txt,sha256=ykGI1ArwOPHqm2g5Cqh3ENdMxEej_a_FcOUov5EM5Oc,155
|
|
103
|
+
dycw_utilities-0.174.17.dist-info/METADATA,sha256=vF_qKrCXt5r90r7OjWFLitGHfDgcAcVUDj6Y47YMvEQ,1710
|
|
104
|
+
dycw_utilities-0.174.17.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/subprocess.py
CHANGED
|
@@ -17,7 +17,8 @@ from typing import IO, TYPE_CHECKING, Literal, assert_never, overload, override
|
|
|
17
17
|
from utilities.errors import ImpossibleCaseError
|
|
18
18
|
from utilities.iterables import always_iterable
|
|
19
19
|
from utilities.logging import to_logger
|
|
20
|
-
from utilities.permissions import ensure_perms
|
|
20
|
+
from utilities.permissions import Permissions, ensure_perms
|
|
21
|
+
from utilities.tempfile import TemporaryDirectory
|
|
21
22
|
from utilities.text import strip_and_dedent
|
|
22
23
|
from utilities.whenever import to_seconds
|
|
23
24
|
|
|
@@ -346,7 +347,7 @@ def rsync(
|
|
|
346
347
|
chown_user: str | None = None,
|
|
347
348
|
chown_group: str | None = None,
|
|
348
349
|
exclude: MaybeIterable[str] | None = None,
|
|
349
|
-
chmod:
|
|
350
|
+
chmod: PermissionsLike | None = None,
|
|
350
351
|
) -> None:
|
|
351
352
|
"""Remote & local file copying."""
|
|
352
353
|
mkdir_args = maybe_sudo_cmd(*mkdir_cmd(dest, parent=True), sudo=sudo) # skipif-ci
|
|
@@ -361,13 +362,13 @@ def rsync(
|
|
|
361
362
|
retry=retry,
|
|
362
363
|
logger=logger,
|
|
363
364
|
)
|
|
364
|
-
|
|
365
|
+
srcs = list(always_iterable(src_or_srcs)) # skipif-ci
|
|
365
366
|
rsync_args = rsync_cmd( # skipif-ci
|
|
366
|
-
|
|
367
|
+
srcs,
|
|
367
368
|
user,
|
|
368
369
|
hostname,
|
|
369
370
|
dest,
|
|
370
|
-
archive=is_dir,
|
|
371
|
+
archive=any(Path(s).is_dir() for s in srcs),
|
|
371
372
|
chown_user=chown_user,
|
|
372
373
|
chown_group=chown_group,
|
|
373
374
|
exclude=exclude,
|
|
@@ -375,7 +376,6 @@ def rsync(
|
|
|
375
376
|
host_key_algorithms=host_key_algorithms,
|
|
376
377
|
strict_host_key_checking=strict_host_key_checking,
|
|
377
378
|
sudo=sudo,
|
|
378
|
-
parent=is_dir,
|
|
379
379
|
)
|
|
380
380
|
run(*rsync_args, print=print, retry=retry, logger=logger) # skipif-ci
|
|
381
381
|
if chmod is not None: # skipif-ci
|
|
@@ -408,7 +408,6 @@ def rsync_cmd(
|
|
|
408
408
|
host_key_algorithms: list[str] = _HOST_KEY_ALGORITHMS,
|
|
409
409
|
strict_host_key_checking: bool = True,
|
|
410
410
|
sudo: bool = False,
|
|
411
|
-
parent: bool = False,
|
|
412
411
|
) -> list[str]:
|
|
413
412
|
"""Command to use 'rsync' to do remote & local file copying."""
|
|
414
413
|
args: list[str] = ["rsync"]
|
|
@@ -438,12 +437,141 @@ def rsync_cmd(
|
|
|
438
437
|
args.extend(["--rsh", join(rsh_args)])
|
|
439
438
|
if sudo:
|
|
440
439
|
args.extend(["--rsync-path", join(sudo_cmd("rsync"))])
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
440
|
+
srcs = list(always_iterable(src_or_srcs)) # do not Path()
|
|
441
|
+
if len(srcs) == 0:
|
|
442
|
+
raise RsyncCmdNoSourcesError(user=user, hostname=hostname, dest=dest)
|
|
443
|
+
missing = [s for s in srcs if not Path(s).exists()]
|
|
444
|
+
if len(missing) >= 1:
|
|
445
|
+
raise RsyncCmdSourcesNotFoundError(
|
|
446
|
+
sources=missing, user=user, hostname=hostname, dest=dest
|
|
447
|
+
)
|
|
448
|
+
return [*args, *map(str, srcs), f"{user}@{hostname}:{dest}"]
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@dataclass(kw_only=True, slots=True)
|
|
452
|
+
class RsyncCmdError(Exception):
|
|
453
|
+
user: str
|
|
454
|
+
hostname: str
|
|
455
|
+
dest: PathLike
|
|
456
|
+
|
|
457
|
+
@override
|
|
458
|
+
def __str__(self) -> str:
|
|
459
|
+
return f"No sources selected to send to {self.user}@{self.hostname}:{self.dest}"
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
@dataclass(kw_only=True, slots=True)
|
|
463
|
+
class RsyncCmdNoSourcesError(RsyncCmdError):
|
|
464
|
+
@override
|
|
465
|
+
def __str__(self) -> str:
|
|
466
|
+
return f"No sources selected to send to {self.user}@{self.hostname}:{self.dest}"
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@dataclass(kw_only=True, slots=True)
|
|
470
|
+
class RsyncCmdSourcesNotFoundError(RsyncCmdError):
|
|
471
|
+
sources: list[PathLike]
|
|
472
|
+
|
|
473
|
+
@override
|
|
474
|
+
def __str__(self) -> str:
|
|
475
|
+
desc = ", ".join(map(repr, map(str, self.sources)))
|
|
476
|
+
return f"Sources selected to send to {self.user}@{self.hostname}:{self.dest} but not found: {desc}"
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
##
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def rsync_many(
|
|
483
|
+
user: str,
|
|
484
|
+
hostname: str,
|
|
485
|
+
/,
|
|
486
|
+
*items: tuple[PathLike, PathLike]
|
|
487
|
+
| tuple[Literal["sudo"], PathLike, PathLike]
|
|
488
|
+
| tuple[PathLike, PathLike, PermissionsLike],
|
|
489
|
+
retry: Retry | None = None,
|
|
490
|
+
logger: LoggerLike | None = None,
|
|
491
|
+
keep: bool = False,
|
|
492
|
+
batch_mode: bool = True,
|
|
493
|
+
host_key_algorithms: list[str] = _HOST_KEY_ALGORITHMS,
|
|
494
|
+
strict_host_key_checking: bool = True,
|
|
495
|
+
print: bool = False, # noqa: A002
|
|
496
|
+
) -> None:
|
|
497
|
+
cmds: list[list[str]] = [] # skipif-ci
|
|
498
|
+
with ( # skipif-ci
|
|
499
|
+
TemporaryDirectory() as temp_src,
|
|
500
|
+
yield_ssh_temp_dir(
|
|
501
|
+
user, hostname, retry=retry, logger=logger, keep=keep
|
|
502
|
+
) as temp_dest,
|
|
503
|
+
):
|
|
504
|
+
for item in items:
|
|
505
|
+
match item:
|
|
506
|
+
case Path() | str() as src, Path() | str() as dest:
|
|
507
|
+
cmds.extend(_rsync_many_prepare(src, dest, temp_src, temp_dest))
|
|
508
|
+
case "sudo", Path() | str() as src, Path() | str() as dest:
|
|
509
|
+
cmds.extend(
|
|
510
|
+
_rsync_many_prepare(src, dest, temp_src, temp_dest, sudo=True)
|
|
511
|
+
)
|
|
512
|
+
case (
|
|
513
|
+
Path() | str() as src,
|
|
514
|
+
Path() | str() as dest,
|
|
515
|
+
Permissions() | int() | str() as perms,
|
|
516
|
+
):
|
|
517
|
+
cmds.extend(
|
|
518
|
+
_rsync_many_prepare(src, dest, temp_src, temp_dest, perms=perms)
|
|
519
|
+
)
|
|
520
|
+
case never:
|
|
521
|
+
assert_never(never)
|
|
522
|
+
rsync(
|
|
523
|
+
f"{temp_src}/",
|
|
524
|
+
user,
|
|
525
|
+
hostname,
|
|
526
|
+
temp_dest,
|
|
527
|
+
batch_mode=batch_mode,
|
|
528
|
+
host_key_algorithms=host_key_algorithms,
|
|
529
|
+
strict_host_key_checking=strict_host_key_checking,
|
|
530
|
+
print=print,
|
|
531
|
+
retry=retry,
|
|
532
|
+
logger=logger,
|
|
533
|
+
)
|
|
534
|
+
ssh(
|
|
535
|
+
user,
|
|
536
|
+
hostname,
|
|
537
|
+
*BASH_LS,
|
|
538
|
+
input="\n".join(map(join, cmds)),
|
|
539
|
+
print=print,
|
|
540
|
+
retry=retry,
|
|
541
|
+
logger=logger,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def _rsync_many_prepare(
|
|
546
|
+
src: PathLike,
|
|
547
|
+
dest: PathLike,
|
|
548
|
+
temp_src: PathLike,
|
|
549
|
+
temp_dest: PathLike,
|
|
550
|
+
/,
|
|
551
|
+
*,
|
|
552
|
+
sudo: bool = False,
|
|
553
|
+
perms: PermissionsLike | None = None,
|
|
554
|
+
) -> list[list[str]]:
|
|
555
|
+
dest, temp_src, temp_dest = map(Path, [dest, temp_src, temp_dest])
|
|
556
|
+
n = len(list(temp_src.iterdir()))
|
|
557
|
+
name = str(n)
|
|
558
|
+
match src:
|
|
559
|
+
case Path():
|
|
560
|
+
cp(src, temp_src / name)
|
|
561
|
+
case str():
|
|
562
|
+
if Path(src).exists():
|
|
563
|
+
cp(src, temp_src / name)
|
|
564
|
+
else:
|
|
565
|
+
tee(temp_src / name, src)
|
|
566
|
+
case never:
|
|
567
|
+
assert_never(never)
|
|
568
|
+
cmds: list[list[str]] = [
|
|
569
|
+
maybe_sudo_cmd(*mkdir_cmd(dest, parent=True), sudo=sudo),
|
|
570
|
+
maybe_sudo_cmd(*cp_cmd(temp_dest / name, dest), sudo=sudo),
|
|
446
571
|
]
|
|
572
|
+
if perms is not None:
|
|
573
|
+
cmds.append(maybe_sudo_cmd(*chmod_cmd(dest, perms), sudo=sudo))
|
|
574
|
+
return cmds
|
|
447
575
|
|
|
448
576
|
|
|
449
577
|
##
|
|
@@ -1019,6 +1147,9 @@ __all__ = [
|
|
|
1019
1147
|
"ChownCmdError",
|
|
1020
1148
|
"CpError",
|
|
1021
1149
|
"MvFileError",
|
|
1150
|
+
"RsyncCmdError",
|
|
1151
|
+
"RsyncCmdNoSourcesError",
|
|
1152
|
+
"RsyncCmdSourcesNotFoundError",
|
|
1022
1153
|
"apt_install_cmd",
|
|
1023
1154
|
"cd_cmd",
|
|
1024
1155
|
"chmod",
|
|
@@ -1041,6 +1172,7 @@ __all__ = [
|
|
|
1041
1172
|
"rm_cmd",
|
|
1042
1173
|
"rsync",
|
|
1043
1174
|
"rsync_cmd",
|
|
1175
|
+
"rsync_many",
|
|
1044
1176
|
"run",
|
|
1045
1177
|
"set_hostname_cmd",
|
|
1046
1178
|
"ssh",
|
|
File without changes
|