dycw-utilities 0.175.17__py3-none-any.whl → 0.175.19__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.17.dist-info → dycw_utilities-0.175.19.dist-info}/METADATA +1 -1
- {dycw_utilities-0.175.17.dist-info → dycw_utilities-0.175.19.dist-info}/RECORD +7 -7
- utilities/__init__.py +1 -1
- utilities/subprocess.py +73 -2
- utilities/tempfile.py +40 -31
- {dycw_utilities-0.175.17.dist-info → dycw_utilities-0.175.19.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.175.17.dist-info → dycw_utilities-0.175.19.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
utilities/__init__.py,sha256=
|
|
1
|
+
utilities/__init__.py,sha256=Icf2ql4qG7C1V1W6jJzw-EbFjcuaZSkx7-OyX1zNNMY,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
|
|
@@ -80,8 +80,8 @@ 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=
|
|
84
|
-
utilities/tempfile.py,sha256=
|
|
83
|
+
utilities/subprocess.py,sha256=F4sRJq9eNnXwZ46GgYxTQQCT-YUx_ghZ4o91dH4KgW4,45405
|
|
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
|
|
87
87
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
|
@@ -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.19.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
101
|
+
dycw_utilities-0.175.19.dist-info/entry_points.txt,sha256=cOGtKeJI0KXLSV7MJ8Dhc2G8jPgDcBDm53MVNJU4ycI,136
|
|
102
|
+
dycw_utilities-0.175.19.dist-info/METADATA,sha256=-Ng54LQmTd_U_-Vbb0rRL7bAKisLDxfPhIrEkrxtT9U,1443
|
|
103
|
+
dycw_utilities-0.175.19.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/subprocess.py
CHANGED
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
from contextlib import contextmanager
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from io import StringIO
|
|
8
|
+
from itertools import repeat
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from re import search
|
|
10
11
|
from shlex import join
|
|
@@ -54,11 +55,36 @@ UPDATE_CA_CERTIFICATES: str = "update-ca-certificates"
|
|
|
54
55
|
##
|
|
55
56
|
|
|
56
57
|
|
|
58
|
+
def append_text(
|
|
59
|
+
path: PathLike,
|
|
60
|
+
text: str,
|
|
61
|
+
/,
|
|
62
|
+
*,
|
|
63
|
+
sudo: bool = False,
|
|
64
|
+
skip_if_present: bool = False,
|
|
65
|
+
flags: int = 0,
|
|
66
|
+
blank_lines: int = 1,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Append text to a file."""
|
|
69
|
+
try:
|
|
70
|
+
existing = cat(path, sudo=sudo)
|
|
71
|
+
except (CalledProcessError, FileNotFoundError):
|
|
72
|
+
tee(path, text, sudo=sudo, append=True)
|
|
73
|
+
return
|
|
74
|
+
if skip_if_present and (search(text, existing, flags=flags) is not None):
|
|
75
|
+
return
|
|
76
|
+
full = "".join([*repeat("\n", times=blank_lines), text])
|
|
77
|
+
tee(path, full, sudo=sudo, append=True)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
|
|
82
|
+
|
|
57
83
|
def apt_install(package: str, /, *, update: bool = False, sudo: bool = False) -> None:
|
|
58
84
|
"""Install a package."""
|
|
59
85
|
if update: # pragma: no cover
|
|
60
86
|
run(*maybe_sudo_cmd(*APT_UPDATE, sudo=sudo))
|
|
61
|
-
run(*maybe_sudo_cmd(*apt_install_cmd(package), sudo=sudo))
|
|
87
|
+
run(*maybe_sudo_cmd(*apt_install_cmd(package), sudo=sudo)) # pragma: no cover
|
|
62
88
|
|
|
63
89
|
|
|
64
90
|
def apt_install_cmd(package: str, /) -> list[str]:
|
|
@@ -69,6 +95,13 @@ def apt_install_cmd(package: str, /) -> list[str]:
|
|
|
69
95
|
##
|
|
70
96
|
|
|
71
97
|
|
|
98
|
+
def cat(path: PathLike, /, *, sudo: bool = False) -> str:
|
|
99
|
+
"""Concatenate a file."""
|
|
100
|
+
if sudo: # pragma: no cover
|
|
101
|
+
return run(*sudo_cmd(*cat_cmd(path)), return_=True)
|
|
102
|
+
return Path(path).read_text()
|
|
103
|
+
|
|
104
|
+
|
|
72
105
|
def cat_cmd(path: PathLike, /) -> list[str]:
|
|
73
106
|
"""Command to use 'cat' to concatenate and print files."""
|
|
74
107
|
return ["cat", str(path)]
|
|
@@ -170,6 +203,24 @@ def chpasswd(user_name: str, password: str, /, *, sudo: bool = False) -> None:
|
|
|
170
203
|
##
|
|
171
204
|
|
|
172
205
|
|
|
206
|
+
def copy_text(
|
|
207
|
+
src: PathLike,
|
|
208
|
+
dest: PathLike,
|
|
209
|
+
/,
|
|
210
|
+
*,
|
|
211
|
+
sudo: bool = False,
|
|
212
|
+
substitutions: StrMapping | None = None,
|
|
213
|
+
) -> None:
|
|
214
|
+
"""Copy the text contents of a file."""
|
|
215
|
+
text = cat(src, sudo=sudo)
|
|
216
|
+
if substitutions is not None:
|
|
217
|
+
text = Template(text).substitute(**substitutions)
|
|
218
|
+
tee(dest, text, sudo=sudo)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
##
|
|
222
|
+
|
|
223
|
+
|
|
173
224
|
def cp(
|
|
174
225
|
src: PathLike,
|
|
175
226
|
dest: PathLike,
|
|
@@ -356,6 +407,20 @@ def mv_cmd(src: PathLike, dest: PathLike, /) -> list[str]:
|
|
|
356
407
|
##
|
|
357
408
|
|
|
358
409
|
|
|
410
|
+
def replace_text(
|
|
411
|
+
path: PathLike, /, *replacements: tuple[str, str], sudo: bool = False
|
|
412
|
+
) -> None:
|
|
413
|
+
"""Replace the text in a file."""
|
|
414
|
+
path = Path(path)
|
|
415
|
+
text = cat(path, sudo=sudo)
|
|
416
|
+
for old, new in replacements:
|
|
417
|
+
text = text.replace(old, new)
|
|
418
|
+
tee(path, text, sudo=sudo)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
##
|
|
422
|
+
|
|
423
|
+
|
|
359
424
|
def ripgrep(*args: str, path: PathLike = PWD) -> str | None:
|
|
360
425
|
"""Search for lines."""
|
|
361
426
|
try: # skipif-ci
|
|
@@ -632,6 +697,7 @@ def _rsync_many_prepare(
|
|
|
632
697
|
case never:
|
|
633
698
|
assert_never(never)
|
|
634
699
|
cmds: list[list[str]] = [
|
|
700
|
+
maybe_sudo_cmd(*rm_cmd(dest), sudo=sudo),
|
|
635
701
|
maybe_sudo_cmd(*mkdir_cmd(dest, parent=True), sudo=sudo),
|
|
636
702
|
maybe_sudo_cmd(*cp_cmd(temp_dest / name, dest), sudo=sudo),
|
|
637
703
|
]
|
|
@@ -1259,7 +1325,8 @@ def symlink_cmd(target: PathLike, link: PathLike, /) -> list[str]:
|
|
|
1259
1325
|
def tee(
|
|
1260
1326
|
path: PathLike, text: str, /, *, sudo: bool = False, append: bool = False
|
|
1261
1327
|
) -> None:
|
|
1262
|
-
"""
|
|
1328
|
+
"""Duplicate standard input."""
|
|
1329
|
+
mkdir(path, sudo=sudo, parent=True)
|
|
1263
1330
|
if sudo: # pragma: no cover
|
|
1264
1331
|
run(*sudo_cmd(*tee_cmd(path, append=append)), input=text)
|
|
1265
1332
|
else:
|
|
@@ -1514,14 +1581,17 @@ __all__ = [
|
|
|
1514
1581
|
"RsyncCmdError",
|
|
1515
1582
|
"RsyncCmdNoSourcesError",
|
|
1516
1583
|
"RsyncCmdSourcesNotFoundError",
|
|
1584
|
+
"append_text",
|
|
1517
1585
|
"apt_install",
|
|
1518
1586
|
"apt_install_cmd",
|
|
1587
|
+
"cat",
|
|
1519
1588
|
"cd_cmd",
|
|
1520
1589
|
"chmod",
|
|
1521
1590
|
"chmod_cmd",
|
|
1522
1591
|
"chown",
|
|
1523
1592
|
"chown_cmd",
|
|
1524
1593
|
"chpasswd",
|
|
1594
|
+
"copy_text",
|
|
1525
1595
|
"cp",
|
|
1526
1596
|
"cp_cmd",
|
|
1527
1597
|
"echo_cmd",
|
|
@@ -1538,6 +1608,7 @@ __all__ = [
|
|
|
1538
1608
|
"mkdir_cmd",
|
|
1539
1609
|
"mv",
|
|
1540
1610
|
"mv_cmd",
|
|
1611
|
+
"replace_text",
|
|
1541
1612
|
"ripgrep",
|
|
1542
1613
|
"ripgrep_cmd",
|
|
1543
1614
|
"rm",
|
utilities/tempfile.py
CHANGED
|
@@ -73,23 +73,44 @@ class _TemporaryDirectoryNoResourceWarning(tempfile.TemporaryDirectory):
|
|
|
73
73
|
@contextmanager
|
|
74
74
|
def TemporaryFile( # noqa: N802
|
|
75
75
|
*,
|
|
76
|
+
dir: PathLike | None = None, # noqa: A002
|
|
76
77
|
suffix: str | None = None,
|
|
77
78
|
prefix: str | None = None,
|
|
78
|
-
dir: PathLike | None = None, # noqa: A002
|
|
79
79
|
ignore_cleanup_errors: bool = False,
|
|
80
80
|
delete: bool = True,
|
|
81
81
|
name: str | None = None,
|
|
82
82
|
text: str | None = None,
|
|
83
83
|
) -> Iterator[Path]:
|
|
84
84
|
"""Yield a temporary file."""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
if dir is None:
|
|
86
|
+
with (
|
|
87
|
+
TemporaryDirectory(
|
|
88
|
+
suffix=suffix,
|
|
89
|
+
prefix=prefix,
|
|
90
|
+
dir=dir,
|
|
91
|
+
ignore_cleanup_errors=ignore_cleanup_errors,
|
|
92
|
+
delete=delete,
|
|
93
|
+
) as temp_dir,
|
|
94
|
+
_temporary_file_outer(
|
|
95
|
+
temp_dir, delete=delete, name=name, text=text
|
|
96
|
+
) as temp,
|
|
97
|
+
):
|
|
98
|
+
yield temp
|
|
99
|
+
else:
|
|
100
|
+
with _temporary_file_outer(dir, delete=delete, name=name, text=text) as temp:
|
|
101
|
+
yield temp
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@contextmanager
|
|
105
|
+
def _temporary_file_outer(
|
|
106
|
+
path: PathLike,
|
|
107
|
+
/,
|
|
108
|
+
*,
|
|
109
|
+
delete: bool = True,
|
|
110
|
+
name: str | None = None,
|
|
111
|
+
text: str | None = None,
|
|
112
|
+
) -> Iterator[Path]:
|
|
113
|
+
with _temporary_file_inner(path, delete=delete, name=name) as temp:
|
|
93
114
|
if text is not None:
|
|
94
115
|
_ = temp.write_text(text)
|
|
95
116
|
yield temp
|
|
@@ -97,29 +118,17 @@ def TemporaryFile( # noqa: N802
|
|
|
97
118
|
|
|
98
119
|
@contextmanager
|
|
99
120
|
def _temporary_file_inner(
|
|
100
|
-
*,
|
|
101
|
-
suffix: str | None = None,
|
|
102
|
-
prefix: str | None = None,
|
|
103
|
-
dir: PathLike | None = None, # noqa: A002
|
|
104
|
-
ignore_cleanup_errors: bool = False,
|
|
105
|
-
delete: bool = True,
|
|
106
|
-
name: str | None = None,
|
|
121
|
+
path: PathLike, /, *, delete: bool = True, name: str | None = None
|
|
107
122
|
) -> Iterator[Path]:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
)
|
|
118
|
-
if name is None:
|
|
119
|
-
yield temp_dir / temp_file.name
|
|
120
|
-
else:
|
|
121
|
-
_ = move(temp_dir / temp_file.name, temp_dir / name)
|
|
122
|
-
yield temp_dir / name
|
|
123
|
+
path = Path(path)
|
|
124
|
+
temp = _NamedTemporaryFile( # noqa: SIM115
|
|
125
|
+
dir=path, delete=delete, delete_on_close=False
|
|
126
|
+
)
|
|
127
|
+
if name is None:
|
|
128
|
+
yield path / temp.name
|
|
129
|
+
else:
|
|
130
|
+
_ = move(path / temp.name, path / name)
|
|
131
|
+
yield path / name
|
|
123
132
|
|
|
124
133
|
|
|
125
134
|
##
|
|
File without changes
|
|
File without changes
|