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.
- machineconfig/cluster/cloud_manager.py +0 -3
- machineconfig/cluster/data_transfer.py +0 -1
- machineconfig/cluster/file_manager.py +0 -1
- machineconfig/cluster/job_params.py +0 -3
- machineconfig/cluster/loader_runner.py +0 -3
- machineconfig/cluster/remote_machine.py +0 -1
- machineconfig/cluster/script_notify_upon_completion.py +0 -1
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +5 -6
- machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
- machineconfig/cluster/sessions_managers/wt_local.py +36 -110
- machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
- machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
- machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
- machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
- machineconfig/cluster/templates/cli_click.py +0 -1
- machineconfig/cluster/templates/cli_gooey.py +0 -2
- machineconfig/cluster/templates/cli_trogon.py +0 -1
- machineconfig/cluster/templates/run_cloud.py +0 -1
- machineconfig/cluster/templates/run_cluster.py +0 -1
- machineconfig/cluster/templates/run_remote.py +0 -1
- machineconfig/cluster/templates/utils.py +27 -46
- machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
- machineconfig/jobs/python/check_installations.py +2 -1
- machineconfig/jobs/python/create_bootable_media.py +0 -2
- machineconfig/jobs/python/python_ve_symlink.py +9 -11
- machineconfig/jobs/python/tasks.py +0 -1
- machineconfig/jobs/python/vscode/api.py +5 -5
- machineconfig/jobs/python/vscode/link_ve.py +13 -14
- machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
- machineconfig/jobs/python/vscode/sync_code.py +9 -13
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
- machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
- machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
- machineconfig/jobs/python_custom_installers/dev/code.py +12 -32
- machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
- machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
- machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
- machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
- machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
- machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
- machineconfig/jobs/python_custom_installers/docker.py +12 -21
- machineconfig/jobs/python_custom_installers/gh.py +11 -19
- machineconfig/jobs/python_custom_installers/hx.py +32 -16
- machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_generic_installers/config.json +1 -1
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
- machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
- machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
- machineconfig/jobs/windows/start_terminal.ps1 +1 -1
- machineconfig/profile/create.py +38 -26
- machineconfig/profile/create_hardlinks.py +29 -20
- machineconfig/profile/shell.py +56 -32
- machineconfig/scripts/__init__.py +0 -2
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/cloud/init.sh +2 -2
- machineconfig/scripts/linux/checkout_versions +1 -1
- machineconfig/scripts/linux/choose_wezterm_theme +1 -1
- machineconfig/scripts/linux/cloud_copy +1 -1
- machineconfig/scripts/linux/cloud_manager +1 -1
- machineconfig/scripts/linux/cloud_mount +1 -1
- machineconfig/scripts/linux/cloud_repo_sync +1 -1
- machineconfig/scripts/linux/cloud_sync +1 -1
- machineconfig/scripts/linux/croshell +1 -1
- machineconfig/scripts/linux/devops +7 -7
- machineconfig/scripts/linux/fire +1 -1
- machineconfig/scripts/linux/fire_agents +3 -2
- machineconfig/scripts/linux/ftpx +1 -1
- machineconfig/scripts/linux/gh_models +1 -1
- machineconfig/scripts/linux/kill_process +1 -1
- machineconfig/scripts/linux/mcinit +1 -1
- machineconfig/scripts/linux/repos +1 -1
- machineconfig/scripts/linux/scheduler +1 -1
- machineconfig/scripts/linux/start_slidev +1 -1
- machineconfig/scripts/linux/start_terminals +1 -1
- machineconfig/scripts/linux/url2md +1 -1
- machineconfig/scripts/linux/warp-cli.sh +122 -0
- machineconfig/scripts/linux/wifi_conn +1 -1
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__init__.py +0 -0
- machineconfig/scripts/python/ai/generate_files.py +83 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
- machineconfig/scripts/python/ai/mcinit.py +14 -7
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +7 -7
- machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
- machineconfig/scripts/python/cloud_copy.py +23 -14
- machineconfig/scripts/python/cloud_mount.py +36 -24
- machineconfig/scripts/python/cloud_repo_sync.py +40 -27
- machineconfig/scripts/python/cloud_sync.py +4 -4
- machineconfig/scripts/python/croshell.py +40 -29
- machineconfig/scripts/python/devops.py +45 -27
- machineconfig/scripts/python/devops_add_identity.py +15 -25
- machineconfig/scripts/python/devops_add_ssh_key.py +8 -8
- machineconfig/scripts/python/devops_backup_retrieve.py +18 -16
- machineconfig/scripts/python/devops_devapps_install.py +25 -20
- machineconfig/scripts/python/devops_update_repos.py +232 -59
- machineconfig/scripts/python/dotfile.py +17 -15
- machineconfig/scripts/python/fire_agents.py +48 -22
- machineconfig/scripts/python/fire_jobs.py +93 -58
- machineconfig/scripts/python/ftpx.py +26 -15
- machineconfig/scripts/python/get_zellij_cmd.py +8 -7
- machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
- machineconfig/scripts/python/helpers/helpers2.py +27 -16
- machineconfig/scripts/python/helpers/helpers4.py +45 -32
- machineconfig/scripts/python/helpers/helpers5.py +1 -1
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +32 -10
- machineconfig/scripts/python/mount_nfs.py +9 -16
- machineconfig/scripts/python/mount_nw_drive.py +10 -5
- machineconfig/scripts/python/mount_ssh.py +9 -7
- machineconfig/scripts/python/repos.py +216 -58
- machineconfig/scripts/python/snapshot.py +0 -1
- machineconfig/scripts/python/start_slidev.py +11 -6
- machineconfig/scripts/python/start_terminals.py +22 -16
- machineconfig/scripts/python/viewer_template.py +0 -1
- machineconfig/scripts/python/wifi_conn.py +49 -75
- machineconfig/scripts/python/wsl_windows_transfer.py +9 -7
- machineconfig/scripts/windows/checkout_version.ps1 +1 -3
- machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
- machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
- machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
- machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
- machineconfig/scripts/windows/croshell.ps1 +2 -2
- machineconfig/scripts/windows/devops.ps1 +1 -4
- machineconfig/scripts/windows/dotfile.ps1 +1 -3
- machineconfig/scripts/windows/fire.ps1 +1 -1
- machineconfig/scripts/windows/ftpx.ps1 +2 -2
- machineconfig/scripts/windows/gpt.ps1 +1 -1
- machineconfig/scripts/windows/kill_process.ps1 +1 -2
- machineconfig/scripts/windows/mcinit.ps1 +1 -1
- machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
- machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
- machineconfig/scripts/windows/pomodoro.ps1 +1 -1
- machineconfig/scripts/windows/py2exe.ps1 +1 -3
- machineconfig/scripts/windows/repos.ps1 +1 -1
- machineconfig/scripts/windows/scheduler.ps1 +1 -1
- machineconfig/scripts/windows/snapshot.ps1 +2 -2
- machineconfig/scripts/windows/start_slidev.ps1 +1 -1
- machineconfig/scripts/windows/start_terminals.ps1 +1 -1
- machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
- machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
- machineconfig/settings/lf/linux/lfrc +2 -1
- machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
- machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
- machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
- machineconfig/settings/svim/linux/init.toml +1 -1
- machineconfig/settings/svim/windows/init.toml +1 -1
- machineconfig/setup_linux/web_shortcuts/croshell.sh +3 -52
- machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
- machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
- machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
- machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
- machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +58 -13
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +45 -37
- machineconfig/utils/ai/generate_file_checklist.py +8 -10
- machineconfig/utils/ai/url2md.py +4 -2
- machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
- machineconfig/utils/cloud/onedrive/transaction.py +63 -98
- machineconfig/utils/code.py +62 -41
- machineconfig/utils/installer.py +29 -35
- machineconfig/utils/installer_utils/installer_abc.py +11 -11
- machineconfig/utils/installer_utils/installer_class.py +155 -74
- machineconfig/utils/links.py +112 -31
- machineconfig/utils/notifications.py +211 -0
- machineconfig/utils/options.py +41 -42
- machineconfig/utils/path.py +13 -6
- machineconfig/utils/path_reduced.py +614 -311
- machineconfig/utils/procs.py +48 -42
- machineconfig/utils/scheduling.py +0 -1
- machineconfig/utils/source_of_truth.py +27 -0
- machineconfig/utils/ssh.py +146 -85
- machineconfig/utils/terminal.py +84 -37
- machineconfig/utils/upgrade_packages.py +91 -0
- machineconfig/utils/utils2.py +39 -50
- machineconfig/utils/utils5.py +195 -116
- machineconfig/utils/ve.py +13 -5
- {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/METADATA +14 -13
- {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/RECORD +212 -237
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/archive/python_tools.txt +0 -12
- machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/update.py +0 -3
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/linux/activate_ve +0 -87
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/windows/activate_ve.ps1 +0 -54
- machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
- machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
- machineconfig/utils/utils.py +0 -95
- /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
- {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/WHEEL +0 -0
- {machineconfig-2.0.dist-info → machineconfig-2.2.dist-info}/top_level.txt +0 -0
machineconfig/utils/terminal.py
CHANGED
|
@@ -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:
|
|
43
|
+
def op(self) -> str:
|
|
44
|
+
return self.output.stdout
|
|
45
|
+
|
|
42
46
|
@property
|
|
43
|
-
def ip(self) -> str:
|
|
47
|
+
def ip(self) -> str:
|
|
48
|
+
return self.output.stdin
|
|
49
|
+
|
|
44
50
|
@property
|
|
45
|
-
def err(self) -> str:
|
|
51
|
+
def err(self) -> str:
|
|
52
|
+
return self.output.stderr
|
|
53
|
+
|
|
46
54
|
@property
|
|
47
|
-
def returncode(self) -> int:
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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(
|
|
107
|
-
|
|
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":
|
|
115
|
-
|
|
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
|
|
153
|
+
start_cmd = "bash"
|
|
122
154
|
full_command = f"{start_cmd} {script_file}" # full_command = [start_cmd, str(script_file)]
|
|
123
|
-
else:
|
|
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":
|
|
127
|
-
|
|
128
|
-
elif platform.system() == "
|
|
129
|
-
|
|
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:
|
|
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 =
|
|
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
|
|
150
|
-
{
|
|
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
|
-
|
|
153
|
-
|
|
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 ==
|
|
159
|
-
try:
|
|
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
|
-
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
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):
|
|
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}")
|
machineconfig/utils/utils2.py
CHANGED
|
@@ -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
|
|
18
|
+
return "".join(random.choices(population, k=length))
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def read_ini(path:
|
|
23
|
-
if not Path(path).exists() or Path(path).is_dir():
|
|
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
|
-
|
|
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=
|
|
35
|
+
mydict = json.loads(Path(path).read_text(encoding="utf-8"), **kwargs)
|
|
32
36
|
except Exception:
|
|
33
37
|
import pyjson5
|
|
34
|
-
|
|
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
|
-
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_toml(path: "Path"):
|
|
38
45
|
import tomli
|
|
39
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|