dycw-utilities 0.175.18__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dycw-utilities
3
- Version: 0.175.18
3
+ Version: 0.175.19
4
4
  Summary: Miscellaneous Python utilities
5
5
  Author: Derek Wan
6
6
  Author-email: Derek Wan <d.wan@icloud.com>
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=psCN55v4h2RM9fNitaqX4CE9cRd60bDV0qAa6_nnbEw,61
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,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=ySVxt8V5WcEsrsy6cdqiK4kb15wGfkQxpxePJM_NCe0,43843
83
+ utilities/subprocess.py,sha256=F4sRJq9eNnXwZ46GgYxTQQCT-YUx_ghZ4o91dH4KgW4,45405
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.18.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
101
- dycw_utilities-0.175.18.dist-info/entry_points.txt,sha256=cOGtKeJI0KXLSV7MJ8Dhc2G8jPgDcBDm53MVNJU4ycI,136
102
- dycw_utilities-0.175.18.dist-info/METADATA,sha256=NfSdn67Xz5devAMMKWOQknDOuGRphL-0POujF8Vv8FM,1443
103
- dycw_utilities-0.175.18.dist-info/RECORD,,
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
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.175.18"
3
+ __version__ = "0.175.19"
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
@@ -1260,7 +1325,7 @@ def symlink_cmd(target: PathLike, link: PathLike, /) -> list[str]:
1260
1325
  def tee(
1261
1326
  path: PathLike, text: str, /, *, sudo: bool = False, append: bool = False
1262
1327
  ) -> None:
1263
- """Use 'tee' to duplicate standard input."""
1328
+ """Duplicate standard input."""
1264
1329
  mkdir(path, sudo=sudo, parent=True)
1265
1330
  if sudo: # pragma: no cover
1266
1331
  run(*sudo_cmd(*tee_cmd(path, append=append)), input=text)
@@ -1516,14 +1581,17 @@ __all__ = [
1516
1581
  "RsyncCmdError",
1517
1582
  "RsyncCmdNoSourcesError",
1518
1583
  "RsyncCmdSourcesNotFoundError",
1584
+ "append_text",
1519
1585
  "apt_install",
1520
1586
  "apt_install_cmd",
1587
+ "cat",
1521
1588
  "cd_cmd",
1522
1589
  "chmod",
1523
1590
  "chmod_cmd",
1524
1591
  "chown",
1525
1592
  "chown_cmd",
1526
1593
  "chpasswd",
1594
+ "copy_text",
1527
1595
  "cp",
1528
1596
  "cp_cmd",
1529
1597
  "echo_cmd",
@@ -1540,6 +1608,7 @@ __all__ = [
1540
1608
  "mkdir_cmd",
1541
1609
  "mv",
1542
1610
  "mv_cmd",
1611
+ "replace_text",
1543
1612
  "ripgrep",
1544
1613
  "ripgrep_cmd",
1545
1614
  "rm",