dycw-utilities 0.174.7__py3-none-any.whl → 0.174.8__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.3
2
2
  Name: dycw-utilities
3
- Version: 0.174.7
3
+ Version: 0.174.8
4
4
  Author: Derek Wan
5
5
  Author-email: Derek Wan <d.wan@icloud.com>
6
6
  Requires-Dist: atomicwrites>=1.4.1,<1.5
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=m6IBJ6HmwjCqt8xNdi4aHkF4CjRw4nrxq8SpNIQAq2Q,60
1
+ utilities/__init__.py,sha256=n95VfhS7rnwwdfXlNQHT0VSWZoAJAyyrDnWLplcWMZ8,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=1mcFSteDnzIfFZT-UOWPg8lBjDKjRWvgSs5hzHXvsts,22231
83
+ utilities/subprocess.py,sha256=dIBguvLB0WepJKqgBxlzF6r8z9HW5JiGKLg-eslpuec,22549
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.7.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
101
- dycw_utilities-0.174.7.dist-info/entry_points.txt,sha256=ykGI1ArwOPHqm2g5Cqh3ENdMxEej_a_FcOUov5EM5Oc,155
102
- dycw_utilities-0.174.7.dist-info/METADATA,sha256=q0-Hb1ta7Jppeysc4MdeoUaTPr3Nqwwb0uiMzysigw8,1709
103
- dycw_utilities-0.174.7.dist-info/RECORD,,
100
+ dycw_utilities-0.174.8.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
101
+ dycw_utilities-0.174.8.dist-info/entry_points.txt,sha256=ykGI1ArwOPHqm2g5Cqh3ENdMxEej_a_FcOUov5EM5Oc,155
102
+ dycw_utilities-0.174.8.dist-info/METADATA,sha256=Y6jmazzTFERRUb55Cbl5cwt3sCw5yThYY7Rrcn1llw0,1709
103
+ dycw_utilities-0.174.8.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.174.7"
3
+ __version__ = "0.174.8"
utilities/subprocess.py CHANGED
@@ -5,7 +5,7 @@ from contextlib import contextmanager
5
5
  from dataclasses import dataclass
6
6
  from io import StringIO
7
7
  from pathlib import Path
8
- from shlex import join, quote
8
+ from shlex import join
9
9
  from string import Template
10
10
  from subprocess import PIPE, CalledProcessError, Popen
11
11
  from threading import Thread
@@ -40,22 +40,37 @@ RESTART_SSHD = ["systemctl", "restart", "sshd"]
40
40
  UPDATE_CA_CERTIFICATES: str = "update-ca-certificates"
41
41
 
42
42
 
43
+ ##
44
+
45
+
43
46
  def apt_install_cmd(package: str, /) -> list[str]:
44
47
  return ["apt", "install", "-y", package]
45
48
 
46
49
 
50
+ ##
51
+
52
+
47
53
  def cat_cmd(path: PathLike, /) -> list[str]:
48
54
  return ["cat", str(path)]
49
55
 
50
56
 
57
+ ##
58
+
59
+
51
60
  def cd_cmd(path: PathLike, /) -> list[str]:
52
61
  return ["cd", str(path)]
53
62
 
54
63
 
64
+ ##
65
+
66
+
55
67
  def chmod_cmd(path: PathLike, mode: str, /) -> list[str]:
56
68
  return ["chmod", mode, str(path)]
57
69
 
58
70
 
71
+ ##
72
+
73
+
59
74
  def chown_cmd(
60
75
  path: PathLike, /, *, user: str | None = None, group: str | None = None
61
76
  ) -> list[str]:
@@ -80,14 +95,23 @@ class ChownCmdError(Exception):
80
95
  return "At least one of 'user' and/or 'group' must be given; got None"
81
96
 
82
97
 
98
+ ##
99
+
100
+
83
101
  def cp_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
84
102
  return ["cp", "-r", str(src), str(dest)]
85
103
 
86
104
 
105
+ ##
106
+
107
+
87
108
  def echo_cmd(text: str, /) -> list[str]:
88
109
  return ["echo", text]
89
110
 
90
111
 
112
+ ##
113
+
114
+
91
115
  def expand_path(
92
116
  path: PathLike, /, *, subs: StrMapping | None = None, sudo: bool = False
93
117
  ) -> Path:
@@ -98,47 +122,71 @@ def expand_path(
98
122
  return Path(path).expanduser()
99
123
 
100
124
 
125
+ ##
126
+
127
+
101
128
  def git_clone_cmd(url: str, path: PathLike, /) -> list[str]:
102
129
  return ["git", "clone", "--recurse-submodules", url, str(path)]
103
130
 
104
131
 
132
+ ##
133
+
134
+
105
135
  def git_hard_reset_cmd(*, branch: str | None = None) -> list[str]:
106
136
  branch_use = "master" if branch is None else branch
107
137
  return ["git", "hard-reset", branch_use]
108
138
 
109
139
 
140
+ ##
141
+
142
+
143
+ def maybe_parent(path: PathLike, /, *, parent: bool = False) -> Path:
144
+ path = Path(path)
145
+ return path.parent if parent else path
146
+
147
+
148
+ ##
149
+
150
+
110
151
  def maybe_sudo_cmd(cmd: str, /, *args: str, sudo: bool = False) -> list[str]:
111
152
  parts: list[str] = [cmd, *args]
112
153
  return sudo_cmd(*parts) if sudo else parts
113
154
 
114
155
 
156
+ ##
157
+
158
+
115
159
  def mkdir(path: PathLike, /, *, sudo: bool = False, parent: bool = False) -> None:
116
160
  if sudo: # pragma: no cover
117
161
  run(*sudo_cmd(*mkdir_cmd(path, parent=parent)))
118
162
  else:
119
- path = expand_path(path)
120
- path_use = path.parent if parent else path
121
- path_use.mkdir(parents=True, exist_ok=True)
163
+ maybe_parent(path, parent=parent).mkdir(parents=True, exist_ok=True)
164
+
165
+
166
+ ##
122
167
 
123
168
 
124
169
  def mkdir_cmd(path: PathLike, /, *, parent: bool = False) -> list[str]:
125
- args: list[str] = ["mkdir", "-p"]
126
- quoted = quote(str(path))
127
- if parent:
128
- args.append(f"$(dirname {quoted})")
129
- else:
130
- args.append(quoted)
131
- return args
170
+ return ["mkdir", "-p", str(maybe_parent(path, parent=parent))]
171
+
172
+
173
+ ##
132
174
 
133
175
 
134
176
  def mv_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
135
177
  return ["mv", str(src), str(dest)]
136
178
 
137
179
 
180
+ ##
181
+
182
+
138
183
  def rm_cmd(path: PathLike, /) -> list[str]:
139
184
  return ["rm", "-rf", str(path)]
140
185
 
141
186
 
187
+ ##
188
+
189
+
142
190
  def rsync(
143
191
  src_or_srcs: MaybeIterable[PathLike],
144
192
  user: str,
@@ -153,7 +201,6 @@ def rsync(
153
201
  print: bool = False, # noqa: A002
154
202
  retry: Retry | None = None,
155
203
  logger: LoggerLike | None = None,
156
- archive: bool = False,
157
204
  chown_user: str | None = None,
158
205
  chown_group: str | None = None,
159
206
  exclude: MaybeIterable[str] | None = None,
@@ -171,12 +218,13 @@ def rsync(
171
218
  retry=retry,
172
219
  logger=logger,
173
220
  )
221
+ is_dir = any(Path(s).is_dir() for s in always_iterable(src_or_srcs)) # skipif-ci
174
222
  rsync_args = rsync_cmd( # skipif-ci
175
223
  src_or_srcs,
176
224
  user,
177
225
  hostname,
178
226
  dest,
179
- archive=archive,
227
+ archive=is_dir,
180
228
  chown_user=chown_user,
181
229
  chown_group=chown_group,
182
230
  exclude=exclude,
@@ -184,6 +232,7 @@ def rsync(
184
232
  host_key_algorithms=host_key_algorithms,
185
233
  strict_host_key_checking=strict_host_key_checking,
186
234
  sudo=sudo,
235
+ parent=is_dir,
187
236
  )
188
237
  run(*rsync_args, print=print, retry=retry, logger=logger) # skipif-ci
189
238
  if chmod is not None: # skipif-ci
@@ -201,6 +250,9 @@ def rsync(
201
250
  )
202
251
 
203
252
 
253
+ ##
254
+
255
+
204
256
  def rsync_cmd(
205
257
  src_or_srcs: MaybeIterable[PathLike],
206
258
  user: str,
@@ -216,6 +268,7 @@ def rsync_cmd(
216
268
  host_key_algorithms: list[str] = _HOST_KEY_ALGORITHMS,
217
269
  strict_host_key_checking: bool = True,
218
270
  sudo: bool = False,
271
+ parent: bool = False,
219
272
  ) -> list[str]:
220
273
  args: list[str] = ["rsync"]
221
274
  if archive:
@@ -244,7 +297,15 @@ def rsync_cmd(
244
297
  args.extend(["--rsh", join(rsh_args)])
245
298
  if sudo:
246
299
  args.extend(["--rsync-path", join(sudo_cmd("rsync"))])
247
- return [*args, *map(str, always_iterable(src_or_srcs)), f"{user}@{hostname}:{dest}"]
300
+ dest_use = maybe_parent(dest, parent=parent)
301
+ return [
302
+ *args,
303
+ *map(str, always_iterable(src_or_srcs)),
304
+ f"{user}@{hostname}:{dest_use}",
305
+ ]
306
+
307
+
308
+ ##
248
309
 
249
310
 
250
311
  @overload
@@ -502,10 +563,16 @@ def _run_write_to_streams(text: str, /, *outputs: IO[str]) -> None:
502
563
  _ = output.write(text)
503
564
 
504
565
 
566
+ ##
567
+
568
+
505
569
  def set_hostname_cmd(hostname: str, /) -> list[str]:
506
570
  return ["hostnamectl", "set-hostname", hostname]
507
571
 
508
572
 
573
+ ##
574
+
575
+
509
576
  @overload
510
577
  def ssh(
511
578
  user: str,
@@ -641,6 +708,9 @@ def ssh(
641
708
  )
642
709
 
643
710
 
711
+ ##
712
+
713
+
644
714
  def ssh_cmd(
645
715
  user: str,
646
716
  hostname: str,
@@ -658,6 +728,9 @@ def ssh_cmd(
658
728
  return [*args, f"{user}@{hostname}", *cmd_and_cmds_or_args]
659
729
 
660
730
 
731
+ ##
732
+
733
+
661
734
  def ssh_opts_cmd(
662
735
  *,
663
736
  batch_mode: bool = True,
@@ -673,26 +746,44 @@ def ssh_opts_cmd(
673
746
  return [*args, "-T"]
674
747
 
675
748
 
749
+ ##
750
+
751
+
676
752
  def ssh_keygen_cmd(hostname: str, /) -> list[str]:
677
753
  return ["ssh-keygen", "-f", "~/.ssh/known_hosts", "-R", hostname]
678
754
 
679
755
 
756
+ ##
757
+
758
+
680
759
  def sudo_cmd(cmd: str, /, *args: str) -> list[str]:
681
760
  return ["sudo", cmd, *args]
682
761
 
683
762
 
763
+ ##
764
+
765
+
684
766
  def sudo_nopasswd_cmd(user: str, /) -> str:
685
767
  return f"{user} ALL=(ALL) NOPASSWD: ALL"
686
768
 
687
769
 
770
+ ##
771
+
772
+
688
773
  def symlink_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
689
774
  return ["ln", "-s", str(src), str(dest)]
690
775
 
691
776
 
777
+ ##
778
+
779
+
692
780
  def touch_cmd(path: PathLike, /) -> list[str]:
693
781
  return ["touch", str(path)]
694
782
 
695
783
 
784
+ ##
785
+
786
+
696
787
  def uv_run_cmd(module: str, /, *args: str) -> list[str]:
697
788
  return [
698
789
  "uv",
@@ -708,6 +799,9 @@ def uv_run_cmd(module: str, /, *args: str) -> list[str]:
708
799
  ]
709
800
 
710
801
 
802
+ ##
803
+
804
+
711
805
  @contextmanager
712
806
  def yield_ssh_temp_dir(
713
807
  user: str,
@@ -748,6 +842,7 @@ __all__ = [
748
842
  "expand_path",
749
843
  "git_clone_cmd",
750
844
  "git_hard_reset_cmd",
845
+ "maybe_parent",
751
846
  "maybe_sudo_cmd",
752
847
  "mkdir",
753
848
  "mkdir_cmd",