machineconfig 2.0__py3-none-any.whl → 2.2__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.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (253) hide show
  1. machineconfig/cluster/cloud_manager.py +0 -3
  2. machineconfig/cluster/data_transfer.py +0 -1
  3. machineconfig/cluster/file_manager.py +0 -1
  4. machineconfig/cluster/job_params.py +0 -3
  5. machineconfig/cluster/loader_runner.py +0 -3
  6. machineconfig/cluster/remote_machine.py +0 -1
  7. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  8. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +5 -6
  9. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
  11. machineconfig/cluster/sessions_managers/wt_local.py +36 -110
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
  13. machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
  20. machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
  30. machineconfig/cluster/templates/cli_click.py +0 -1
  31. machineconfig/cluster/templates/cli_gooey.py +0 -2
  32. machineconfig/cluster/templates/cli_trogon.py +0 -1
  33. machineconfig/cluster/templates/run_cloud.py +0 -1
  34. machineconfig/cluster/templates/run_cluster.py +0 -1
  35. machineconfig/cluster/templates/run_remote.py +0 -1
  36. machineconfig/cluster/templates/utils.py +27 -46
  37. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  38. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  39. machineconfig/jobs/python/check_installations.py +2 -1
  40. machineconfig/jobs/python/create_bootable_media.py +0 -2
  41. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  42. machineconfig/jobs/python/tasks.py +0 -1
  43. machineconfig/jobs/python/vscode/api.py +5 -5
  44. machineconfig/jobs/python/vscode/link_ve.py +13 -14
  45. machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
  46. machineconfig/jobs/python/vscode/sync_code.py +9 -13
  47. machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
  48. machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
  49. machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
  50. machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
  51. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
  52. machineconfig/jobs/python_custom_installers/dev/code.py +12 -32
  53. machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
  54. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
  55. machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
  56. machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
  57. machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
  58. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
  59. machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
  60. machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
  61. machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
  62. machineconfig/jobs/python_custom_installers/docker.py +12 -21
  63. machineconfig/jobs/python_custom_installers/gh.py +11 -19
  64. machineconfig/jobs/python_custom_installers/hx.py +32 -16
  65. machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
  66. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  67. machineconfig/jobs/python_generic_installers/config.json +1 -1
  68. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  70. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  71. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  72. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  73. machineconfig/profile/create.py +38 -26
  74. machineconfig/profile/create_hardlinks.py +29 -20
  75. machineconfig/profile/shell.py +56 -32
  76. machineconfig/scripts/__init__.py +0 -2
  77. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  78. machineconfig/scripts/cloud/init.sh +2 -2
  79. machineconfig/scripts/linux/checkout_versions +1 -1
  80. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  81. machineconfig/scripts/linux/cloud_copy +1 -1
  82. machineconfig/scripts/linux/cloud_manager +1 -1
  83. machineconfig/scripts/linux/cloud_mount +1 -1
  84. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  85. machineconfig/scripts/linux/cloud_sync +1 -1
  86. machineconfig/scripts/linux/croshell +1 -1
  87. machineconfig/scripts/linux/devops +7 -7
  88. machineconfig/scripts/linux/fire +1 -1
  89. machineconfig/scripts/linux/fire_agents +3 -2
  90. machineconfig/scripts/linux/ftpx +1 -1
  91. machineconfig/scripts/linux/gh_models +1 -1
  92. machineconfig/scripts/linux/kill_process +1 -1
  93. machineconfig/scripts/linux/mcinit +1 -1
  94. machineconfig/scripts/linux/repos +1 -1
  95. machineconfig/scripts/linux/scheduler +1 -1
  96. machineconfig/scripts/linux/start_slidev +1 -1
  97. machineconfig/scripts/linux/start_terminals +1 -1
  98. machineconfig/scripts/linux/url2md +1 -1
  99. machineconfig/scripts/linux/warp-cli.sh +122 -0
  100. machineconfig/scripts/linux/wifi_conn +1 -1
  101. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
  106. machineconfig/scripts/python/ai/__init__.py +0 -0
  107. machineconfig/scripts/python/ai/generate_files.py +83 -0
  108. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
  109. machineconfig/scripts/python/ai/mcinit.py +14 -7
  110. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
  111. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  112. machineconfig/scripts/python/archive/tmate_start.py +7 -7
  113. machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
  114. machineconfig/scripts/python/cloud_copy.py +23 -14
  115. machineconfig/scripts/python/cloud_mount.py +36 -24
  116. machineconfig/scripts/python/cloud_repo_sync.py +40 -27
  117. machineconfig/scripts/python/cloud_sync.py +4 -4
  118. machineconfig/scripts/python/croshell.py +40 -29
  119. machineconfig/scripts/python/devops.py +45 -27
  120. machineconfig/scripts/python/devops_add_identity.py +15 -25
  121. machineconfig/scripts/python/devops_add_ssh_key.py +8 -8
  122. machineconfig/scripts/python/devops_backup_retrieve.py +18 -16
  123. machineconfig/scripts/python/devops_devapps_install.py +25 -20
  124. machineconfig/scripts/python/devops_update_repos.py +232 -59
  125. machineconfig/scripts/python/dotfile.py +17 -15
  126. machineconfig/scripts/python/fire_agents.py +48 -22
  127. machineconfig/scripts/python/fire_jobs.py +93 -58
  128. machineconfig/scripts/python/ftpx.py +26 -15
  129. machineconfig/scripts/python/get_zellij_cmd.py +8 -7
  130. machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
  131. machineconfig/scripts/python/helpers/helpers2.py +27 -16
  132. machineconfig/scripts/python/helpers/helpers4.py +45 -32
  133. machineconfig/scripts/python/helpers/helpers5.py +1 -1
  134. machineconfig/scripts/python/helpers/repo_sync_helpers.py +32 -10
  135. machineconfig/scripts/python/mount_nfs.py +9 -16
  136. machineconfig/scripts/python/mount_nw_drive.py +10 -5
  137. machineconfig/scripts/python/mount_ssh.py +9 -7
  138. machineconfig/scripts/python/repos.py +216 -58
  139. machineconfig/scripts/python/snapshot.py +0 -1
  140. machineconfig/scripts/python/start_slidev.py +11 -6
  141. machineconfig/scripts/python/start_terminals.py +22 -16
  142. machineconfig/scripts/python/viewer_template.py +0 -1
  143. machineconfig/scripts/python/wifi_conn.py +49 -75
  144. machineconfig/scripts/python/wsl_windows_transfer.py +9 -7
  145. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  146. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  147. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  148. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  149. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  150. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  151. machineconfig/scripts/windows/croshell.ps1 +2 -2
  152. machineconfig/scripts/windows/devops.ps1 +1 -4
  153. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  154. machineconfig/scripts/windows/fire.ps1 +1 -1
  155. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  156. machineconfig/scripts/windows/gpt.ps1 +1 -1
  157. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  158. machineconfig/scripts/windows/mcinit.ps1 +1 -1
  159. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  160. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  161. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  162. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  163. machineconfig/scripts/windows/repos.ps1 +1 -1
  164. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  165. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  166. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  167. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  168. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  169. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  170. machineconfig/settings/lf/linux/lfrc +2 -1
  171. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  172. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  173. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  174. machineconfig/settings/svim/linux/init.toml +1 -1
  175. machineconfig/settings/svim/windows/init.toml +1 -1
  176. machineconfig/setup_linux/web_shortcuts/croshell.sh +3 -52
  177. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  178. machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
  179. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  180. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  181. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  182. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  183. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  184. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +58 -13
  185. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +45 -37
  186. machineconfig/utils/ai/generate_file_checklist.py +8 -10
  187. machineconfig/utils/ai/url2md.py +4 -2
  188. machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
  189. machineconfig/utils/cloud/onedrive/transaction.py +63 -98
  190. machineconfig/utils/code.py +62 -41
  191. machineconfig/utils/installer.py +29 -35
  192. machineconfig/utils/installer_utils/installer_abc.py +11 -11
  193. machineconfig/utils/installer_utils/installer_class.py +155 -74
  194. machineconfig/utils/links.py +112 -31
  195. machineconfig/utils/notifications.py +211 -0
  196. machineconfig/utils/options.py +41 -42
  197. machineconfig/utils/path.py +13 -6
  198. machineconfig/utils/path_reduced.py +614 -311
  199. machineconfig/utils/procs.py +48 -42
  200. machineconfig/utils/scheduling.py +0 -1
  201. machineconfig/utils/source_of_truth.py +27 -0
  202. machineconfig/utils/ssh.py +146 -85
  203. machineconfig/utils/terminal.py +84 -37
  204. machineconfig/utils/upgrade_packages.py +91 -0
  205. machineconfig/utils/utils2.py +39 -50
  206. machineconfig/utils/utils5.py +195 -116
  207. machineconfig/utils/ve.py +13 -5
  208. {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/METADATA +14 -13
  209. {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/RECORD +212 -237
  210. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  211. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  212. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  213. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  214. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  215. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  216. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  217. machineconfig/jobs/python_generic_installers/update.py +0 -3
  218. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  219. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  220. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  221. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  222. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  223. machineconfig/scripts/linux/activate_ve +0 -87
  224. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  225. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  226. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  227. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  228. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  229. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  230. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  231. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  232. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  233. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  234. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  235. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  236. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  237. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  238. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  239. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  240. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  241. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  242. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  243. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  244. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  245. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  246. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  247. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  248. machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
  249. machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
  250. machineconfig/utils/utils.py +0 -95
  251. /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
  252. {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/WHEEL +0 -0
  253. {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
1
-
2
- from machineconfig.utils.path_reduced import P, OPLike
1
+ from machineconfig.utils.path_reduced import PathExtended, OPLike
3
2
  import subprocess
4
3
  from typing import Any, BinaryIO, Optional, Union
5
4
  import platform
@@ -29,48 +28,70 @@ class Response:
29
28
  resp.output.stderr = cp.stderr
30
29
  resp.output.returncode = cp.returncode
31
30
  return resp
31
+
32
32
  def __init__(self, stdin: Optional[BinaryIO] = None, stdout: Optional[BinaryIO] = None, stderr: Optional[BinaryIO] = None, cmd: Optional[str] = None, desc: str = ""):
33
33
  self.std = dict(stdin=stdin, stdout=stdout, stderr=stderr)
34
34
  self.output = STD(stdin="", stdout="", stderr="", returncode=0)
35
35
  self.input = cmd
36
36
  self.desc = desc # input command
37
+
37
38
  def __call__(self, *args: Any, **kwargs: Any) -> Optional[str]:
38
39
  _ = args, kwargs
39
40
  return self.op.rstrip() if type(self.op) is str else None
41
+
40
42
  @property
41
- def op(self) -> str: return self.output.stdout
43
+ def op(self) -> str:
44
+ return self.output.stdout
45
+
42
46
  @property
43
- def ip(self) -> str: return self.output.stdin
47
+ def ip(self) -> str:
48
+ return self.output.stdin
49
+
44
50
  @property
45
- def err(self) -> str: return self.output.stderr
51
+ def err(self) -> str:
52
+ return self.output.stderr
53
+
46
54
  @property
47
- def returncode(self) -> int: return self.output.returncode
48
- def op2path(self, strict_returncode: bool = True, strict_err: bool = False) -> Union[P, None]:
49
- if self.is_successful(strict_returcode=strict_returncode, strict_err=strict_err): return P(self.op.rstrip())
55
+ def returncode(self) -> int:
56
+ return self.output.returncode
57
+
58
+ def op2path(self, strict_returncode: bool = True, strict_err: bool = False) -> Union[PathExtended, None]:
59
+ if self.is_successful(strict_returcode=strict_returncode, strict_err=strict_err):
60
+ return PathExtended(self.op.rstrip())
50
61
  return None
51
- def op_if_successfull_or_default(self, strict_returcode: bool = True, strict_err: bool = False) -> Optional[str]: return self.op if self.is_successful(strict_returcode=strict_returcode, strict_err=strict_err) else None
62
+
63
+ def op_if_successfull_or_default(self, strict_returcode: bool = True, strict_err: bool = False) -> Optional[str]:
64
+ return self.op if self.is_successful(strict_returcode=strict_returcode, strict_err=strict_err) else None
65
+
52
66
  def is_successful(self, strict_returcode: bool = True, strict_err: bool = False) -> bool:
53
67
  return ((self.returncode in {0, None}) if strict_returcode else True) and (self.err == "" if strict_err else True)
68
+
54
69
  def capture(self):
55
70
  for key in ["stdin", "stdout", "stderr"]:
56
71
  val: Optional[BinaryIO] = self.std[key]
57
72
  if val is not None and val.readable():
58
73
  self.output.__dict__[key] = val.read().decode().rstrip()
59
74
  return self
75
+
60
76
  def print_if_unsuccessful(self, desc: str = "TERMINAL CMD", strict_err: bool = False, strict_returncode: bool = False, assert_success: bool = False):
61
77
  success = self.is_successful(strict_err=strict_err, strict_returcode=strict_returncode)
62
- if assert_success: assert success, self.print(capture=False, desc=desc)
78
+ if assert_success:
79
+ assert success, self.print(capture=False, desc=desc)
63
80
  if success:
64
81
  print(f"✅ {desc} completed successfully")
65
82
  else:
66
83
  self.print(capture=False, desc=desc)
67
84
  return self
85
+
68
86
  def print(self, desc: str = "TERMINAL CMD", capture: bool = True):
69
- if capture: self.capture()
87
+ if capture:
88
+ self.capture()
70
89
  from rich import console
90
+
71
91
  con = console.Console()
72
92
  from rich.panel import Panel
73
93
  from rich.text import Text # from rich.syntax import Syntax; syntax = Syntax(my_code, "python", theme="monokai", line_numbers=True)
94
+
74
95
  tmp1 = Text("📥 Input Command:\n")
75
96
  tmp1.stylize("u bold blue")
76
97
  tmp2 = Text("\n📤 Terminal Response:\n")
@@ -80,10 +101,12 @@ class Response:
80
101
  con.print(Panel(txt, title=f"🖥️ {self.desc}", subtitle=f"📋 {desc}", width=150, style="bold cyan on black"))
81
102
  return self
82
103
 
104
+
83
105
  # DEPRECATED: Use subprocess.run directly instead of Terminal class.
84
106
  # The Terminal class has been replaced with inline subprocess calls to underlying primitives.
85
107
  # This file is kept for reference but should not be used.
86
108
 
109
+
87
110
  class Terminal:
88
111
  def __init__(self, stdout: Optional[int] = subprocess.PIPE, stderr: Optional[int] = subprocess.PIPE, stdin: Optional[int] = subprocess.PIPE, elevated: bool = False):
89
112
  self.machine: str = platform.system()
@@ -91,88 +114,112 @@ class Terminal:
91
114
  self.stdout = stdout
92
115
  self.stderr = stderr
93
116
  self.stdin = stdin
117
+
94
118
  # def set_std_system(self): self.stdout = sys.stdout; self.stderr = sys.stderr; self.stdin = sys.stdin
95
119
  def set_std_pipe(self):
96
120
  self.stdout = subprocess.PIPE
97
121
  self.stderr = subprocess.PIPE
98
122
  self.stdin = subprocess.PIPE
123
+
99
124
  def set_std_null(self):
100
125
  self.stdout, self.stderr, self.stdin = subprocess.DEVNULL, subprocess.DEVNULL, subprocess.DEVNULL # Equivalent to `echo 'foo' &> /dev/null`
126
+
101
127
  def run(self, *cmds: str, shell: Optional[SHELLS] = "default", check: bool = False, ip: Optional[str] = None) -> Response: # Runs SYSTEM commands like subprocess.run
102
128
  """Blocking operation. Thus, if you start a shell via this method, it will run in the main and won't stop until you exit manually IF stdin is set to sys.stdin, otherwise it will run and close quickly. Other combinations of stdin, stdout can lead to funny behaviour like no output but accept input or opposite.
103
129
  * This method is short for: res = subprocess.run("powershell command", capture_output=True, shell=True, text=True) and unlike os.system(cmd), subprocess.run(cmd) gives much more control over the output and input.
104
130
  * `shell=True` loads up the profile of the shell called so more specific commands can be run. Importantly, on Windows, the `start` command becomes availalbe and new windows can be launched.
105
131
  * `capture_output` prevents the stdout to redirect to the stdout of the script automatically, instead it will be stored in the Response object returned. # `capture_output=True` same as `stdout=subprocess.PIPE, stderr=subprocess.PIPE`"""
106
- my_list = list(cmds) # `subprocess.Popen` (process open) is the most general command. Used here to create asynchronous job. `subprocess.run` is a thin wrapper around Popen that makes it wait until it finishes the task. `suprocess.call` is an archaic command for pre-Python-3.5.
107
- if self.machine == "Windows" and shell in {"powershell", "pwsh"}: my_list = [shell, "-Command"] + my_list # alternatively, one can run "cmd"
132
+ my_list = list(
133
+ cmds
134
+ ) # `subprocess.Popen` (process open) is the most general command. Used here to create asynchronous job. `subprocess.run` is a thin wrapper around Popen that makes it wait until it finishes the task. `suprocess.call` is an archaic command for pre-Python-3.5.
135
+ if self.machine == "Windows" and shell in {"powershell", "pwsh"}:
136
+ my_list = [shell, "-Command"] + my_list # alternatively, one can run "cmd"
108
137
  if self.elevated is False or self.is_user_admin():
109
138
  resp: subprocess.CompletedProcess[str] = subprocess.run(my_list, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=check, input=ip)
110
139
  else:
111
140
  resp = __import__("ctypes").windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
112
141
  return Response.from_completed_process(resp)
142
+
113
143
  def run_script(self, script: str, shell: SHELLS = "default", verbose: bool = False):
114
- if self.machine == "Linux": script = "#!/bin/bash" + "\n" + script # `source` is only available in bash.
115
- script_file = P.tmpfile(name="tmp_shell_script", suffix=".ps1" if self.machine == "Windows" else ".sh", folder="tmp_scripts").write_text(script, newline={"Windows": None, "Linux": "\n"}[self.machine])
144
+ if self.machine == "Linux":
145
+ script = "#!/bin/bash" + "\n" + script # `source` is only available in bash.
146
+ script_file = PathExtended.tmpfile(name="tmp_shell_script", suffix=".ps1" if self.machine == "Windows" else ".sh", folder="tmp_scripts")
147
+ script_file.write_text(script, newline={"Windows": None, "Linux": "\n"}[self.machine])
116
148
  if shell == "default":
117
149
  if self.machine == "Windows":
118
150
  start_cmd = "powershell" # default shell on Windows is cmd which is not very useful. (./source is not available)
119
151
  full_command: Union[list[str], str] = [start_cmd, str(script_file)] # shell=True will cause this to be a string anyway (with space separation)
120
152
  else:
121
- start_cmd = "bash"
153
+ start_cmd = "bash"
122
154
  full_command = f"{start_cmd} {script_file}" # full_command = [start_cmd, str(script_file)]
123
- else: full_command = f"{shell} {script_file}" # full_command = [shell, str(tmp_file)]
155
+ else:
156
+ full_command = f"{shell} {script_file}" # full_command = [shell, str(tmp_file)]
124
157
  if verbose:
125
- desc="Script to be executed:"
126
- if platform.system() == "Windows": lexer = "powershell"
127
- elif platform.system() == "Linux": lexer = "sh"
128
- elif platform.system() == "Darwin": lexer = "sh" # macOS uses similar shell to Linux
129
- else: raise NotImplementedError(f"Platform {platform.system()} not supported.")
158
+ desc = "Script to be executed:"
159
+ if platform.system() == "Windows":
160
+ lexer = "powershell"
161
+ elif platform.system() == "Linux":
162
+ lexer = "sh"
163
+ elif platform.system() == "Darwin":
164
+ lexer = "sh" # macOS uses similar shell to Linux
165
+ else:
166
+ raise NotImplementedError(f"Platform {platform.system()} not supported.")
130
167
  from rich.console import Console
131
168
  from rich.panel import Panel
132
169
  from rich.syntax import Syntax
133
170
  import rich.progress as pb
171
+
134
172
  console = Console()
135
173
  console.print(Panel(Syntax(code=script, lexer=lexer), title=f"📄 {desc}"), style="bold red")
136
174
  with pb.Progress(transient=True) as progress:
137
175
  _task = progress.add_task(f"Running Script @ {script_file}", total=None)
138
176
  resp = subprocess.run(full_command, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=False)
139
- else: resp = subprocess.run(full_command, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=False)
177
+ else:
178
+ resp = subprocess.run(full_command, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=False)
140
179
  return Response.from_completed_process(resp)
180
+
141
181
  def run_py(self, script: str, wdir: OPLike = None, interactive: bool = True, ipython: bool = True, shell: Optional[str] = None, terminal: str = "", new_window: bool = True, header: bool = True): # async run, since sync run is meaningless.
142
182
  script = (Terminal.get_header(wdir=wdir, toolbox=True) if header else "") + script + ("\nDisplayData.set_pandas_auto_width()\n" if terminal in {"wt", "powershell", "pwsh"} else "")
143
- py_script = P.tmpfile(name="tmp_python_script", suffix=".py", folder="tmp_scripts/terminal")
183
+ py_script = PathExtended.tmpfile(name="tmp_python_script", suffix=".py", folder="tmp_scripts/terminal")
144
184
  py_script.write_text(f"""print(r'''{script}''')""" + "\n" + script)
145
185
  print(f"""🚀 [ASYNC PYTHON SCRIPT] Script URI:
146
186
  {py_script.absolute().as_uri()}""")
147
187
  print("Script to be executed asyncronously: ", py_script.absolute().as_uri())
148
188
  shell_script = f"""
149
- {f'cd {wdir}' if wdir is not None else ''}
150
- {'ipython' if ipython else 'python'} {'-i' if interactive else ''} {py_script}
189
+ {f"cd {wdir}" if wdir is not None else ""}
190
+ {"ipython" if ipython else "python"} {"-i" if interactive else ""} {py_script}
151
191
  """
152
- shell_script = P.tmpfile(name="tmp_shell_script", suffix=".sh" if self.machine == "Linux" else ".ps1", folder="tmp_scripts/shell").write_text(shell_script)
153
- if shell is None and self.machine == "Windows": shell = "pwsh"
192
+ shell_path = PathExtended.tmpfile(name="tmp_shell_script", suffix=".sh" if self.machine == "Linux" else ".ps1", folder="tmp_scripts/shell")
193
+ shell_path.write_text(shell_script)
194
+ if shell is None and self.machine == "Windows":
195
+ shell = "pwsh"
154
196
  window = "start" if new_window and self.machine == "Windows" else ""
155
197
  os.system(f"{window} {terminal} {shell} {shell_script}")
198
+
156
199
  @staticmethod
157
200
  def is_user_admin() -> bool: # adopted from: https://stackoverflow.com/questions/19672352/how-to-run-script-with-elevated-privilege-on-windows"""
158
- if os.name == 'nt':
159
- try: return __import__("ctypes").windll.shell32.IsUserAnAdmin()
201
+ if os.name == "nt":
202
+ try:
203
+ return __import__("ctypes").windll.shell32.IsUserAnAdmin()
160
204
  except Exception:
161
205
  import traceback
206
+
162
207
  traceback.print_exc()
163
208
  print("Admin check failed, assuming not an admin.")
164
209
  return False
165
210
  else:
166
211
  return os.getuid() == 0 # Check for root on Posix
167
- # @staticmethod
168
- # def run_as_admin(file: PLike, params: Any, wait: bool = False):
169
- # proce_info = install_n_import(library="win32com", package="pywin32", fromlist=["shell.shell.ShellExecuteEx"]).shell.shell.ShellExecuteEx(lpVerb='runas', lpFile=file, lpParameters=params)
170
- # # TODO update PATH for this to take effect immediately.
171
- # if wait: time.sleep(1)
172
- # return proce_info
212
+
213
+ # @staticmethod
214
+ # def run_as_admin(file: PLike, params: Any, wait: bool = False):
215
+ # proce_info = install_n_import(library="win32com", package="pywin32", fromlist=["shell.shell.ShellExecuteEx"]).shell.shell.ShellExecuteEx(lpVerb='runas', lpFile=file, lpParameters=params)
216
+ # # TODO update PATH for this to take effect immediately.
217
+ # if wait: time.sleep(1)
218
+ # return proce_info
173
219
 
174
220
  @staticmethod
175
- def get_header(wdir: OPLike, toolbox: bool): return f"""
221
+ def get_header(wdir: OPLike, toolbox: bool):
222
+ return f"""
176
223
  # >> Code prepended
177
224
  {"from crocodile.toolbox import *" if toolbox else "# No toolbox import."}
178
225
  {'''sys.path.insert(0, r'{wdir}') ''' if wdir is not None else "# No path insertion."}
@@ -0,0 +1,91 @@
1
+ """
2
+ Generate uv add commands from pyproject.toml dependency groups.
3
+ """
4
+
5
+ from pathlib import Path
6
+ import tomllib
7
+ from typing import Any
8
+
9
+
10
+ def generate_uv_add_commands(pyproject_path: Path, output_path: Path) -> None:
11
+ """
12
+ Generate uv add commands for each dependency group in pyproject.toml.
13
+
14
+ Args:
15
+ pyproject_path: Path to the pyproject.toml file
16
+ output_path: Path where to write the uv add commands
17
+ """
18
+ # Read pyproject.toml
19
+ with open(pyproject_path, "rb") as f:
20
+ pyproject_data: dict[str, Any] = tomllib.load(f)
21
+
22
+ commands: list[str] = []
23
+
24
+ # Handle main dependencies (no group)
25
+ if "project" in pyproject_data and "dependencies" in pyproject_data["project"]:
26
+ main_deps = pyproject_data["project"]["dependencies"]
27
+ if main_deps:
28
+ # Extract package names without version constraints
29
+ package_names = [extract_package_name(dep) for dep in main_deps]
30
+ commands.append(f"uv add {' '.join(package_names)}")
31
+
32
+ # Handle optional dependencies as groups
33
+ if "project" in pyproject_data and "optional-dependencies" in pyproject_data["project"]:
34
+ optional_deps = pyproject_data["project"]["optional-dependencies"]
35
+ for group_name, deps in optional_deps.items():
36
+ if deps:
37
+ package_names = [extract_package_name(dep) for dep in deps]
38
+ commands.append(f"uv add {' '.join(package_names)} --group {group_name}")
39
+
40
+ # Handle dependency-groups (like dev)
41
+ if "dependency-groups" in pyproject_data:
42
+ dep_groups = pyproject_data["dependency-groups"]
43
+ for group_name, deps in dep_groups.items():
44
+ if deps:
45
+ package_names = [extract_package_name(dep) for dep in deps]
46
+ if group_name == "dev":
47
+ commands.append(f"uv add {' '.join(package_names)} --dev")
48
+ else:
49
+ commands.append(f"uv add {' '.join(package_names)} --group {group_name}")
50
+
51
+ # Write commands to output file
52
+ with open(output_path, "w") as f:
53
+ for command in commands:
54
+ f.write(command + "\n")
55
+
56
+ print(f"Generated {len(commands)} uv add commands in {output_path}")
57
+
58
+
59
+ def extract_package_name(dependency_spec: str) -> str:
60
+ """
61
+ Extract package name from dependency specification.
62
+
63
+ Examples:
64
+ "rich>=14.0.0" -> "rich"
65
+ "requests>=2.32.5" -> "requests"
66
+ "pywin32" -> "pywin32"
67
+ "package[extra]>=1.0" -> "package"
68
+ """
69
+ # Handle extras like "package[extra]>=1.0" first
70
+ if "[" in dependency_spec:
71
+ dependency_spec = dependency_spec.split("[")[0].strip()
72
+
73
+ # Split on common version operators and take the first part
74
+ for operator in [">=", "<=", "==", "!=", ">", "<", "~=", "===", "@"]:
75
+ if operator in dependency_spec:
76
+ return dependency_spec.split(operator)[0].strip()
77
+
78
+ # Return as-is if no version constraint found
79
+ return dependency_spec.strip()
80
+
81
+
82
+ if __name__ == "__main__":
83
+ # Example usage
84
+ current_dir = Path.cwd()
85
+ pyproject_file = current_dir / "pyproject.toml"
86
+ output_file = current_dir / "uv_add_commands.txt"
87
+
88
+ if pyproject_file.exists():
89
+ generate_uv_add_commands(pyproject_file, output_file)
90
+ else:
91
+ print(f"pyproject.toml not found at {pyproject_file}")
@@ -1,85 +1,74 @@
1
-
2
-
3
1
  from pathlib import Path
4
2
  from typing import Optional, Any
5
- # import time
6
- # from typing import Callable, Literal, TypeVar, ParamSpec
7
3
 
8
4
 
9
5
  def randstr(length: int = 10, lower: bool = True, upper: bool = True, digits: bool = True, punctuation: bool = False, safe: bool = False, noun: bool = False) -> str:
10
6
  if safe:
11
7
  import secrets
8
+
12
9
  return secrets.token_urlsafe(length) # interannly, it uses: random.SystemRandom or os.urandom which is hardware-based, not pseudo
13
10
  if noun:
14
11
  import randomname
12
+
15
13
  return randomname.get_name()
16
14
  import string
17
15
  import random
16
+
18
17
  population = (string.ascii_lowercase if lower else "") + (string.ascii_uppercase if upper else "") + (string.digits if digits else "") + (string.punctuation if punctuation else "")
19
- return ''.join(random.choices(population, k=length))
18
+ return "".join(random.choices(population, k=length))
20
19
 
21
20
 
22
- def read_ini(path: 'Path', encoding: Optional[str] = None):
23
- if not Path(path).exists() or Path(path).is_dir(): raise FileNotFoundError(f"File not found or is a directory: {path}")
21
+ def read_ini(path: "Path", encoding: Optional[str] = None):
22
+ if not Path(path).exists() or Path(path).is_dir():
23
+ raise FileNotFoundError(f"File not found or is a directory: {path}")
24
24
  import configparser
25
+
25
26
  res = configparser.ConfigParser()
26
27
  res.read(filenames=[str(path)], encoding=encoding)
27
28
  return res
28
- def read_json(path: 'Path', r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
29
+
30
+
31
+ def read_json(path: "Path", r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
29
32
  import json
33
+
30
34
  try:
31
- mydict = json.loads(Path(path).read_text(encoding='utf-8'), **kwargs)
35
+ mydict = json.loads(Path(path).read_text(encoding="utf-8"), **kwargs)
32
36
  except Exception:
33
37
  import pyjson5
34
- mydict = pyjson5.loads(Path(path).read_text(encoding='utf-8'), **kwargs) # file has C-style comments.
38
+
39
+ mydict = pyjson5.loads(Path(path).read_text(encoding="utf-8"), **kwargs) # file has C-style comments.
35
40
  _ = r
36
41
  return mydict
37
- def read_toml(path: 'Path'):
42
+
43
+
44
+ def read_toml(path: "Path"):
38
45
  import tomli
39
- return tomli.loads(path.read_text(encoding='utf-8'))
46
+
47
+ return tomli.loads(path.read_text(encoding="utf-8"))
48
+
40
49
 
41
50
  def pprint(obj: dict[Any, Any], title: str) -> None:
42
51
  from rich import inspect
52
+
43
53
  inspect(type("TempStruct", (object,), obj)(), value=False, title=title, docs=False, dunder=False, sort=False)
54
+
55
+
44
56
  def get_repr(obj: dict[Any, Any], sep: str = "\n", justify: int = 15, quotes: bool = False):
45
57
  return sep.join([f"{key:>{justify}} = {repr(val) if quotes else val}" for key, val in obj.items()])
46
58
 
47
59
 
48
- # T = TypeVar('T')
49
- # PS = ParamSpec('PS')
50
-
51
- # class RepeatUntilNoException:
52
- # """
53
- # Repeat function calling if it raised an exception and/or exceeded the timeout, for a maximum of `retry` times.
54
- # * Alternative: `https://github.com/jd/tenacity`
55
- # """
56
- # def __init__(self, retry: int, sleep: float, timeout: Optional[float] = None, scaling: Literal["linear", "exponential"] = "exponential"):
57
- # self.retry = retry
58
- # self.sleep = sleep
59
- # self.timeout = timeout
60
- # self.scaling: Literal["linear", "exponential"] = scaling
61
- # def __call__(self, func: Callable[PS, T]) -> Callable[PS, T]:
62
- # from functools import wraps
63
- # if self.timeout is not None:
64
- # import warpt_time_decorator
65
- # func = wrapt_timeout_decorator.timeout(self.timeout)(func)
66
- # @wraps(wrapped=func)
67
- # def wrapper(*args: PS.args, **kwargs: PS.kwargs):
68
- # t0 = time.time()
69
- # for idx in range(self.retry):
70
- # try:
71
- # return func(*args, **kwargs)
72
- # except Exception as ex:
73
- # match self.scaling:
74
- # case "linear":
75
- # sleep_time = self.sleep * (idx + 1)
76
- # case "exponential":
77
- # sleep_time = self.sleep * (idx + 1)**2
78
- # print(f"""💥 [RETRY] Function {func.__name__} call failed with error:
79
- # {ex}
80
- # Retry count: {idx}/{self.retry}. Sleeping for {sleep_time} seconds.
81
- # Total elapsed time: {time.time() - t0:0.1f} seconds.""")
82
- # print(f"""💥 Robust call of `{func}` failed with ```{ex}```.\nretrying {idx}/{self.retry} more times after sleeping for {sleep_time} seconds.\nTotal wait time so far {time.time() - t0: 0.1f} seconds.""")
83
- # time.sleep(sleep_time)
84
- # raise RuntimeError(f"💥 Robust call failed after {self.retry} retries and total wait time of {time.time() - t0: 0.1f} seconds.\n{func=}\n{args=}\n{kwargs=}")
85
- # return wrapper
60
+ def human_friendly_dict(d: dict[str, Any]) -> dict[str, Any]:
61
+ from datetime import datetime
62
+
63
+ result = {}
64
+ for k, v in d.items():
65
+ if isinstance(v, float):
66
+ result[k] = f"{v:.2f}"
67
+ elif isinstance(v, bool):
68
+ result[k] = "" if v else ""
69
+ elif isinstance(v, int) and len(str(v)) == 13 and v > 0: # assuming ms timestamp
70
+ dt = datetime.fromtimestamp(v / 1000)
71
+ result[k] = dt.strftime("%Y-%m-%d %H:%M")
72
+ else:
73
+ result[k] = v
74
+ return result