dycw-utilities 0.174.5__tar.gz → 0.174.6__tar.gz

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.
Files changed (102) hide show
  1. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/PKG-INFO +1 -1
  2. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/pyproject.toml +2 -2
  3. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/__init__.py +1 -1
  4. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/subprocess.py +106 -8
  5. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/README.md +0 -0
  6. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/aeventkit.py +0 -0
  7. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/altair.py +0 -0
  8. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/asyncio.py +0 -0
  9. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/atomicwrites.py +0 -0
  10. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/atools.py +0 -0
  11. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/cachetools.py +0 -0
  12. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/click.py +0 -0
  13. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/concurrent.py +0 -0
  14. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/contextlib.py +0 -0
  15. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/contextvars.py +0 -0
  16. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/cryptography.py +0 -0
  17. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/cvxpy.py +0 -0
  18. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/dataclasses.py +0 -0
  19. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/docker.py +0 -0
  20. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/enum.py +0 -0
  21. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/errors.py +0 -0
  22. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/fastapi.py +0 -0
  23. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/fpdf2.py +0 -0
  24. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/functions.py +0 -0
  25. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/functools.py +0 -0
  26. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/getpass.py +0 -0
  27. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/git.py +0 -0
  28. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/grp.py +0 -0
  29. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/gzip.py +0 -0
  30. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/hashlib.py +0 -0
  31. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/http.py +0 -0
  32. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/hypothesis.py +0 -0
  33. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/importlib.py +0 -0
  34. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/inflect.py +0 -0
  35. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/ipython.py +0 -0
  36. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/iterables.py +0 -0
  37. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/jinja2.py +0 -0
  38. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/json.py +0 -0
  39. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/jupyter.py +0 -0
  40. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/libcst.py +0 -0
  41. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/lightweight_charts.py +0 -0
  42. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/logging.py +0 -0
  43. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/math.py +0 -0
  44. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/memory_profiler.py +0 -0
  45. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/modules.py +0 -0
  46. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/more_itertools.py +0 -0
  47. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/numpy.py +0 -0
  48. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/operator.py +0 -0
  49. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/optuna.py +0 -0
  50. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/orjson.py +0 -0
  51. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/os.py +0 -0
  52. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/parse.py +0 -0
  53. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pathlib.py +0 -0
  54. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pickle.py +0 -0
  55. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/platform.py +0 -0
  56. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/polars.py +0 -0
  57. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/polars_ols.py +0 -0
  58. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/postgres.py +0 -0
  59. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pottery.py +0 -0
  60. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pqdm.py +0 -0
  61. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/psutil.py +0 -0
  62. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pwd.py +0 -0
  63. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/py.typed +0 -0
  64. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pydantic.py +0 -0
  65. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pydantic_settings.py +0 -0
  66. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pydantic_settings_sops.py +0 -0
  67. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pyinstrument.py +0 -0
  68. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pytest.py +0 -0
  69. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pytest_plugins/__init__.py +0 -0
  70. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  71. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  72. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/pytest_regressions.py +0 -0
  73. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/random.py +0 -0
  74. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/re.py +0 -0
  75. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/redis.py +0 -0
  76. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/reprlib.py +0 -0
  77. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/scipy.py +0 -0
  78. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/sentinel.py +0 -0
  79. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/shelve.py +0 -0
  80. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/shutil.py +0 -0
  81. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/slack_sdk.py +0 -0
  82. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/socket.py +0 -0
  83. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/sqlalchemy.py +0 -0
  84. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/sqlalchemy_polars.py +0 -0
  85. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/statsmodels.py +0 -0
  86. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/string.py +0 -0
  87. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/tempfile.py +0 -0
  88. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/testbook.py +0 -0
  89. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/text.py +0 -0
  90. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/threading.py +0 -0
  91. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/timer.py +0 -0
  92. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/traceback.py +0 -0
  93. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/types.py +0 -0
  94. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/typing.py +0 -0
  95. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/tzdata.py +0 -0
  96. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/tzlocal.py +0 -0
  97. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/uuid.py +0 -0
  98. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/version.py +0 -0
  99. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/warnings.py +0 -0
  100. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/whenever.py +0 -0
  101. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/zipfile.py +0 -0
  102. {dycw_utilities-0.174.5 → dycw_utilities-0.174.6}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dycw-utilities
3
- Version: 0.174.5
3
+ Version: 0.174.6
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
@@ -101,7 +101,7 @@
101
101
  name = "dycw-utilities"
102
102
  readme = "README.md"
103
103
  requires-python = ">= 3.12"
104
- version = "0.174.5"
104
+ version = "0.174.6"
105
105
 
106
106
  [project.entry-points.pytest11]
107
107
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -135,7 +135,7 @@
135
135
  # bump-my-version
136
136
  [tool.bumpversion]
137
137
  allow_dirty = true
138
- current_version = "0.174.5"
138
+ current_version = "0.174.6"
139
139
 
140
140
  [[tool.bumpversion.files]]
141
141
  filename = "src/utilities/__init__.py"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.174.5"
3
+ __version__ = "0.174.6"
@@ -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
- _yield_write(proc.stdout, *stdout_outputs),
228
- _yield_write(proc.stderr, *stderr_outputs),
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 _yield_write(input_: IO[str], /, *outputs: IO[str]) -> Iterator[None]:
311
- thread = Thread(target=_run_target, args=(input_, *outputs), daemon=True)
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 _run_target(input_: IO[str], /, *outputs: IO[str]) -> None:
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
- _write_to_streams(text, *outputs)
375
+ _run_write_to_streams(text, *outputs)
323
376
 
324
377
 
325
- def _write_to_streams(text: str, /, *outputs: IO[str]) -> None:
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
  ]