machineconfig 2.1__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 (89) hide show
  1. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +2 -1
  2. machineconfig/cluster/templates/utils.py +0 -35
  3. machineconfig/jobs/python/check_installations.py +1 -1
  4. machineconfig/jobs/python_custom_installers/dev/code.py +0 -13
  5. machineconfig/jobs/python_generic_installers/config.json +1 -1
  6. machineconfig/profile/create.py +10 -5
  7. machineconfig/profile/create_hardlinks.py +3 -1
  8. machineconfig/profile/shell.py +8 -7
  9. machineconfig/scripts/__init__.py +0 -2
  10. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  11. machineconfig/scripts/linux/devops +6 -4
  12. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  13. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  14. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  15. machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
  16. machineconfig/scripts/python/ai/generate_files.py +14 -15
  17. machineconfig/scripts/python/ai/mcinit.py +8 -5
  18. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  19. machineconfig/scripts/python/archive/tmate_start.py +7 -7
  20. machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
  21. machineconfig/scripts/python/cloud_copy.py +22 -13
  22. machineconfig/scripts/python/cloud_mount.py +35 -23
  23. machineconfig/scripts/python/cloud_repo_sync.py +38 -25
  24. machineconfig/scripts/python/cloud_sync.py +4 -4
  25. machineconfig/scripts/python/croshell.py +37 -28
  26. machineconfig/scripts/python/devops.py +45 -27
  27. machineconfig/scripts/python/devops_add_identity.py +15 -25
  28. machineconfig/scripts/python/devops_add_ssh_key.py +7 -7
  29. machineconfig/scripts/python/devops_backup_retrieve.py +17 -15
  30. machineconfig/scripts/python/devops_devapps_install.py +25 -20
  31. machineconfig/scripts/python/devops_update_repos.py +142 -57
  32. machineconfig/scripts/python/dotfile.py +16 -14
  33. machineconfig/scripts/python/fire_agents.py +24 -17
  34. machineconfig/scripts/python/fire_jobs.py +91 -55
  35. machineconfig/scripts/python/ftpx.py +24 -14
  36. machineconfig/scripts/python/get_zellij_cmd.py +8 -7
  37. machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
  38. machineconfig/scripts/python/helpers/helpers2.py +25 -14
  39. machineconfig/scripts/python/helpers/helpers4.py +44 -30
  40. machineconfig/scripts/python/helpers/helpers5.py +1 -1
  41. machineconfig/scripts/python/helpers/repo_sync_helpers.py +31 -9
  42. machineconfig/scripts/python/mount_nfs.py +8 -15
  43. machineconfig/scripts/python/mount_nw_drive.py +10 -5
  44. machineconfig/scripts/python/mount_ssh.py +8 -6
  45. machineconfig/scripts/python/repos.py +215 -57
  46. machineconfig/scripts/python/snapshot.py +0 -1
  47. machineconfig/scripts/python/start_slidev.py +10 -5
  48. machineconfig/scripts/python/start_terminals.py +22 -16
  49. machineconfig/scripts/python/viewer_template.py +0 -1
  50. machineconfig/scripts/python/wifi_conn.py +49 -75
  51. machineconfig/scripts/python/wsl_windows_transfer.py +8 -6
  52. machineconfig/settings/lf/linux/lfrc +1 -0
  53. machineconfig/setup_linux/web_shortcuts/croshell.sh +5 -0
  54. machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
  55. machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
  56. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -12
  57. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -1
  58. machineconfig/utils/code.py +3 -3
  59. machineconfig/utils/installer.py +2 -2
  60. machineconfig/utils/installer_utils/installer_abc.py +3 -4
  61. machineconfig/utils/installer_utils/installer_class.py +6 -4
  62. machineconfig/utils/links.py +103 -33
  63. machineconfig/utils/notifications.py +52 -38
  64. machineconfig/utils/options.py +16 -23
  65. machineconfig/utils/path_reduced.py +239 -205
  66. machineconfig/utils/procs.py +1 -1
  67. machineconfig/utils/source_of_truth.py +27 -0
  68. machineconfig/utils/ssh.py +9 -29
  69. machineconfig/utils/terminal.py +4 -2
  70. machineconfig/utils/upgrade_packages.py +91 -0
  71. machineconfig/utils/utils2.py +1 -2
  72. machineconfig/utils/utils5.py +23 -11
  73. machineconfig/utils/ve.py +4 -1
  74. {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/METADATA +13 -13
  75. {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/RECORD +78 -86
  76. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  77. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  78. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  79. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  80. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  81. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  82. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  83. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  84. machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
  85. machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
  86. machineconfig/utils/utils.py +0 -97
  87. /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
  88. {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/WHEEL +0 -0
  89. {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
-
2
1
  """Utility to launch multiple AI agent prompts in a Zellij session.
3
2
 
4
3
  Improved design notes:
@@ -20,15 +19,17 @@ from machineconfig.utils.utils2 import randstr
20
19
  import random
21
20
  # import time
22
21
 
23
- AGENTS: TypeAlias = Literal["cursor-agent", "gemini", "crush", "q",
24
- # warp terminal
25
- ]
22
+ AGENTS: TypeAlias = Literal[
23
+ "cursor-agent", "gemini", "crush", "q", "onlyPrepPromptFiles"
24
+ # warp terminal
25
+ ]
26
26
  TabConfig = dict[str, tuple[str, str]] # tab name -> (cwd, command)
27
27
  DEFAULT_AGENT_CAP = 6
28
28
 
29
29
 
30
30
  def get_gemini_api_keys() -> list[str]:
31
31
  from machineconfig.utils.utils2 import read_ini
32
+
32
33
  config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
33
34
  res: list[str] = []
34
35
  for a_section_name in list(config.sections()):
@@ -45,12 +46,17 @@ def get_gemini_api_keys() -> list[str]:
45
46
  def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
46
47
  """Return all Python files under repo_root whose text contains keyword.
47
48
 
48
- Errors reading individual files are ignored (decoded with 'ignore').
49
+ Notes:
50
+ - Skips any paths that reside under a directory named ".venv" at any depth.
51
+ - Errors reading individual files are ignored (decoded with 'ignore').
49
52
  """
50
53
  py_files = list(repo_root.rglob("*.py"))
51
54
  keyword_lower = keyword.lower()
52
55
  matches: list[Path] = []
53
56
  for f in py_files:
57
+ # Skip anything under a .venv directory anywhere in the path
58
+ if any(part == ".venv" for part in f.parts):
59
+ continue
54
60
  try:
55
61
  if keyword_lower in f.read_text(encoding="utf-8", errors="ignore").lower():
56
62
  matches.append(f)
@@ -63,6 +69,8 @@ def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
63
69
  def _write_list_file(target: Path, files: Iterable[Path]) -> None:
64
70
  target.parent.mkdir(parents=True, exist_ok=True)
65
71
  target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
72
+
73
+
66
74
  def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
67
75
  prompts = [p for p in prompts if p.strip() != ""] # drop blank entries
68
76
  if len(prompts) <= max_agents:
@@ -73,6 +81,8 @@ def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
73
81
  for i in range(0, len(prompts), chunk_size):
74
82
  grouped.append("\nTargeted Locations:\n".join(prompts[i : i + chunk_size]))
75
83
  return grouped
84
+
85
+
76
86
  def _confirm(message: str, default_no: bool = True) -> bool:
77
87
  suffix = "[y/N]" if default_no else "[Y/n]"
78
88
  answer = input(f"{message} {suffix} ").strip().lower()
@@ -93,11 +103,7 @@ def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_age
93
103
  raise ValueError("No prompts provided")
94
104
 
95
105
  if len(prompts) > max_agents:
96
- proceed = _confirm(
97
- message=(
98
- f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"
99
- )
100
- )
106
+ proceed = _confirm(message=(f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"))
101
107
  if not proceed:
102
108
  print("Aborting per user choice.")
103
109
  return {}
@@ -145,6 +151,10 @@ crush run {prompt_path}
145
151
  case "q":
146
152
  cmd = f"""
147
153
  q chat --no-interactive --trust-all-tools {prompt_path}
154
+ """
155
+ case "onlyPrepPromptFiles":
156
+ cmd = f"""
157
+ echo "Prepared prompt file at {prompt_path}"
148
158
  """
149
159
  case _:
150
160
  raise ValueError(f"Unsupported agent type: {agent}")
@@ -161,7 +171,7 @@ sleep 0.1
161
171
  sleep 0.1
162
172
  echo "---------END OF AGENT OUTPUT---------"
163
173
  """
164
- cmd_path.write_text(cmd_prefix+cmd+cmd_postfix, encoding="utf-8")
174
+ cmd_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
165
175
  fire_cmd = f"bash {shlex.quote(str(cmd_path))}"
166
176
  tab_config[f"Agent{idx}"] = (str(repo_root), fire_cmd)
167
177
 
@@ -208,18 +218,15 @@ def main(): # noqa: C901 - (complexity acceptable for CLI glue)
208
218
  combined_prompts = [prefix + "\n" + p for p in combined_prompts]
209
219
 
210
220
  from machineconfig.utils.options import choose_one_option
221
+
211
222
  agent_selected = choose_one_option(header="Select agent type", options=get_args(AGENTS))
212
223
 
213
- tab_config = launch_agents(
214
- repo_root=repo_root,
215
- prompts=combined_prompts,
216
- agent=agent_selected,
217
- max_agents=DEFAULT_AGENT_CAP,
218
- )
224
+ tab_config = launch_agents(repo_root=repo_root, prompts=combined_prompts, agent=agent_selected, max_agents=DEFAULT_AGENT_CAP)
219
225
  if not tab_config:
220
226
  return
221
227
 
222
228
  from machineconfig.utils.utils2 import randstr
229
+
223
230
  random_name = randstr(length=3)
224
231
  manager = ZellijLocalManager(session2zellij_tabs={"Agents": tab_config}, session_name_prefix=random_name)
225
232
  manager.start_all_sessions()
@@ -7,13 +7,14 @@ fire
7
7
 
8
8
  """
9
9
 
10
-
11
10
  from machineconfig.scripts.python.helpers.helpers4 import search_for_files_of_interest
12
11
  from machineconfig.scripts.python.helpers.helpers4 import convert_kwargs_to_fire_kwargs_str
13
12
  from machineconfig.scripts.python.helpers.helpers4 import parse_pyfile
14
13
  from machineconfig.scripts.python.helpers.helpers4 import get_import_module_code
15
14
  from machineconfig.utils.ve import get_repo_root, get_ve_activate_line, get_ve_path_and_ipython_profile
16
- from machineconfig.utils.utils import display_options, choose_one_option, PROGRAM_PATH, match_file_name, sanitize_path
15
+ from machineconfig.utils.options import display_options, choose_one_option
16
+ from machineconfig.utils.path import match_file_name, sanitize_path
17
+ from machineconfig.utils.source_of_truth import PROGRAM_PATH
17
18
  from machineconfig.utils.path_reduced import PathExtended as PathExtended
18
19
  from machineconfig.utils.io_save import save_toml
19
20
  from machineconfig.utils.utils2 import randstr, read_toml
@@ -28,27 +29,27 @@ str2obj = {"True": True, "False": False, "None": None}
28
29
 
29
30
  def main() -> None:
30
31
  parser = argparse.ArgumentParser()
31
- parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
32
- parser.add_argument("function", nargs='?', type=str, help="Fuction to run", default=None)
33
- parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
34
- parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
35
- parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
36
- parser.add_argument("--debug", "-d", action="store_true", help="debug")
32
+ parser.add_argument("path", nargs="?", type=str, help="The directory containing the jobs", default=".")
33
+ parser.add_argument("function", nargs="?", type=str, help="Fuction to run", default=None)
34
+ parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
35
+ parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
36
+ parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
37
+ parser.add_argument("--debug", "-d", action="store_true", help="debug")
37
38
  parser.add_argument("--choose_function", "-c", action="store_true", help="debug")
38
- parser.add_argument("--loop", "-l", action="store_true", help="infinite recusion (runs again after completion/interruption)")
39
- parser.add_argument("--jupyter", "-j", action="store_true", help="open in a jupyter notebook")
39
+ parser.add_argument("--loop", "-l", action="store_true", help="infinite recusion (runs again after completion/interruption)")
40
+ parser.add_argument("--jupyter", "-j", action="store_true", help="open in a jupyter notebook")
40
41
  parser.add_argument("--submit_to_cloud", "-C", action="store_true", help="submit to cloud compute")
41
- parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
42
- parser.add_argument("--module", "-m", action="store_true", help="launch the main file")
43
- parser.add_argument("--streamlit", "-S", action="store_true", help="run as streamlit app")
44
- parser.add_argument("--environment", "-E", type=str, help="Choose ip, localhost, hostname or arbitrary url", default="")
45
- parser.add_argument("--holdDirectory", "-D", action="store_true", help="hold current directory and avoid cd'ing to the script directory")
46
- parser.add_argument("--PathExport", "-P", action="store_true", help="augment the PYTHONPATH with repo root.")
47
- parser.add_argument("--git_pull", "-g", action="store_true", help="Start by pulling the git repo")
48
- parser.add_argument("--optimized", "-O", action="store_true", help="Run the optimized version of the function")
49
- parser.add_argument("--Nprocess", "-p", type=int, help="Number of processes to use", default=1)
50
- parser.add_argument("--zellij_tab", "-z", type=str, dest="zellij_tab", help="open in a new zellij tab")
51
- parser.add_argument("--watch", "-w", action="store_true", help="watch the file for changes")
42
+ parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
43
+ parser.add_argument("--module", "-m", action="store_true", help="launch the main file")
44
+ parser.add_argument("--streamlit", "-S", action="store_true", help="run as streamlit app")
45
+ parser.add_argument("--environment", "-E", type=str, help="Choose ip, localhost, hostname or arbitrary url", default="")
46
+ parser.add_argument("--holdDirectory", "-D", action="store_true", help="hold current directory and avoid cd'ing to the script directory")
47
+ parser.add_argument("--PathExport", "-P", action="store_true", help="augment the PYTHONPATH with repo root.")
48
+ parser.add_argument("--git_pull", "-g", action="store_true", help="Start by pulling the git repo")
49
+ parser.add_argument("--optimized", "-O", action="store_true", help="Run the optimized version of the function")
50
+ parser.add_argument("--Nprocess", "-p", type=int, help="Number of processes to use", default=1)
51
+ parser.add_argument("--zellij_tab", "-z", type=str, dest="zellij_tab", help="open in a new zellij tab")
52
+ parser.add_argument("--watch", "-w", action="store_true", help="watch the file for changes")
52
53
  parser.add_argument("--kw", nargs="*", default=None, help="keyword arguments to pass to the function in the form of k1 v1 k2 v2 ... (meaning k1=v1, k2=v2, etc)")
53
54
  try:
54
55
  args = parser.parse_args()
@@ -60,7 +61,8 @@ def main() -> None:
60
61
  # print(f"Passed path sanitied to {path_obj}")
61
62
  if not path_obj.exists():
62
63
  path_obj = match_file_name(sub_string=args.path, search_root=PathExtended.cwd())
63
- else: pass
64
+ else:
65
+ pass
64
66
  if path_obj.is_dir():
65
67
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
66
68
  files = search_for_files_of_interest(path_obj)
@@ -73,8 +75,9 @@ def main() -> None:
73
75
  print(f"💾 Selected file: {choice_file}.\nRepo root: {repo_root}")
74
76
 
75
77
  ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
76
- if ipy_profile is None: ipy_profile = "default"
77
- activate_ve_line = get_ve_activate_line(ve_root=args.ve or ve_root_from_file or "$HOME/code/machineconfig/.venv")
78
+ if ipy_profile is None:
79
+ ipy_profile = "default"
80
+ activate_ve_line = get_ve_activate_line(ve_root=args.ve or ve_root_from_file or "$HOME/code/machineconfig/.venv")
78
81
 
79
82
  # Convert args.kw to dictionary
80
83
  if choice_file.suffix == ".py":
@@ -99,19 +102,23 @@ def main() -> None:
99
102
  choice_function_tmp = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
100
103
  assert isinstance(choice_function_tmp, str), f"choice_function must be a string. Got {type(choice_function_tmp)}"
101
104
  choice_index = options.index(choice_function_tmp)
102
- choice_function = choice_function_tmp.split(' -- ')[0]
105
+ choice_function = choice_function_tmp.split(" -- ")[0]
103
106
  choice_function_args = func_args[choice_index]
104
107
 
105
- if choice_function == "RUN AS MAIN": choice_function = None
108
+ if choice_function == "RUN AS MAIN":
109
+ choice_function = None
106
110
  if len(choice_function_args) > 0 and len(kwargs) == 0:
107
111
  for item in choice_function_args:
108
112
  kwargs[item.name] = input(f"Please enter a value for argument `{item.name}` (type = {item.type}) (default = {item.default}) : ") or item.default
109
113
  elif choice_file.suffix == ".sh": # in this case, we choos lines.
110
114
  options = []
111
115
  for line in choice_file.read_text(encoding="utf-8").splitlines():
112
- if line.startswith("#"): continue
113
- if line == "": continue
114
- if line.startswith("echo"): continue
116
+ if line.startswith("#"):
117
+ continue
118
+ if line == "":
119
+ continue
120
+ if line.startswith("echo"):
121
+ continue
115
122
  options.append(line)
116
123
  chosen_lines = display_options(msg="Choose a line to run", options=options, fzf=True, multi=True)
117
124
  choice_file = PathExtended.tmpfile(suffix=".sh")
@@ -124,9 +131,10 @@ def main() -> None:
124
131
  if choice_file.suffix == ".py":
125
132
  if args.streamlit:
126
133
  import socket
134
+
127
135
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
128
136
  try:
129
- s.connect(('8.8.8.8', 1))
137
+ s.connect(("8.8.8.8", 1))
130
138
  local_ip_v4 = s.getsockname()[0]
131
139
  except Exception:
132
140
  local_ip_v4 = socket.gethostbyname(socket.gethostname())
@@ -140,7 +148,8 @@ def main() -> None:
140
148
  toml_path = toml_path_maybe
141
149
  elif choice_file.parent.name == "pages":
142
150
  toml_path_maybe = choice_file.parent.parent.joinpath(".streamlit/config.toml")
143
- if toml_path_maybe.exists(): toml_path = toml_path_maybe
151
+ if toml_path_maybe.exists():
152
+ toml_path = toml_path_maybe
144
153
  if toml_path is not None:
145
154
  print(f"📄 Reading config.toml @ {toml_path}")
146
155
  config = read_toml(toml_path)
@@ -152,10 +161,14 @@ def main() -> None:
152
161
  secrets_template_path = PathExtended.home().joinpath(f"dotfiles/creds/streamlit/{PathExtended(repo_root).name}/{choice_file.name}/secrets.toml")
153
162
  if args.environment != "" and not secrets_path.exists() and secrets_template_path.exists():
154
163
  secrets_template = read_toml(secrets_template_path)
155
- if args.environment == "ip": host_url = f"http://{local_ip_v4}:{port}/oauth2callback"
156
- elif args.environment == "localhost": host_url = f"http://localhost:{port}/oauth2callback"
157
- elif args.environment == "hostname": host_url = f"http://{computer_name}:{port}/oauth2callback"
158
- else: host_url = f"http://{args.environment}:{port}/oauth2callback"
164
+ if args.environment == "ip":
165
+ host_url = f"http://{local_ip_v4}:{port}/oauth2callback"
166
+ elif args.environment == "localhost":
167
+ host_url = f"http://localhost:{port}/oauth2callback"
168
+ elif args.environment == "hostname":
169
+ host_url = f"http://{computer_name}:{port}/oauth2callback"
170
+ else:
171
+ host_url = f"http://{args.environment}:{port}/oauth2callback"
159
172
  try:
160
173
  secrets_template["auth"]["redirect_uri"] = host_url
161
174
  secrets_template["auth"]["cookie_secret"] = randstr(35)
@@ -167,11 +180,14 @@ def main() -> None:
167
180
  message = f"🚀 Streamlit app is running @:\n1- http://{local_ip_v4}:{port}\n2- http://{computer_name}:{port}\n3- http://localhost:{port}"
168
181
  from rich.panel import Panel
169
182
  from rich import print as rprint
183
+
170
184
  rprint(Panel(message))
171
185
  exe = f"streamlit run --server.address 0.0.0.0 --server.headless true --server.port {port}"
172
186
  # exe = f"cd '{choice_file.parent}'; " + exe
173
- elif args.interactive is False: exe = "python"
174
- elif args.jupyter: exe = "jupyter-lab"
187
+ elif args.interactive is False:
188
+ exe = "python"
189
+ elif args.jupyter:
190
+ exe = "jupyter-lab"
175
191
  else:
176
192
  exe = f"ipython -i --no-banner --profile {ipy_profile} "
177
193
  elif choice_file.suffix == ".ps1" or choice_file.suffix == ".sh":
@@ -181,7 +197,9 @@ def main() -> None:
181
197
  else:
182
198
  raise NotImplementedError(f"File type {choice_file.suffix} not supported, in the sense that I don't know how to fire it.")
183
199
 
184
- if args.module or (args.debug and args.choose_function): # because debugging tools do not support choosing functions and don't interplay with fire module. So the only way to have debugging and choose function options is to import the file as a module into a new script and run the function of interest there and debug the new script.
200
+ if (
201
+ args.module or (args.debug and args.choose_function)
202
+ ): # because debugging tools do not support choosing functions and don't interplay with fire module. So the only way to have debugging and choose function options is to import the file as a module into a new script and run the function of interest there and debug the new script.
185
203
  assert choice_file.suffix == ".py", f"File must be a python file to be imported as a module. Got {choice_file}"
186
204
  import_line = get_import_module_code(str(choice_file))
187
205
  if repo_root is not None:
@@ -201,11 +219,15 @@ except (ImportError, ModuleNotFoundError) as ex:
201
219
  print(fr"✅ Successfully imported `{choice_file}`")
202
220
  """
203
221
  if choice_function is not None:
204
- txt = txt + f"""
205
- res = {choice_function}({('**' + str(kwargs)) if kwargs else ''})
222
+ txt = (
223
+ txt
224
+ + f"""
225
+ res = {choice_function}({("**" + str(kwargs)) if kwargs else ""})
206
226
  """
227
+ )
207
228
 
208
- txt = f"""
229
+ txt = (
230
+ f"""
209
231
  try:
210
232
  from rich.panel import Panel
211
233
  from rich.console import Console
@@ -214,8 +236,10 @@ try:
214
236
  console.print(Panel(Syntax(code=r'''{txt}''', lexer='python'), title='Import Script'), style="bold red")
215
237
  except ImportError as _ex:
216
238
  print(r'''{txt}''')
217
- """ + txt
218
- choice_file = PathExtended.tmp().joinpath(f'tmp_scripts/python/{PathExtended(choice_file).parent.name}_{PathExtended(choice_file).stem}_{randstr()}.py')
239
+ """
240
+ + txt
241
+ )
242
+ choice_file = PathExtended.tmp().joinpath(f"tmp_scripts/python/{PathExtended(choice_file).parent.name}_{PathExtended(choice_file).stem}_{randstr()}.py")
219
243
  choice_file.parent.mkdir(parents=True, exist_ok=True)
220
244
  choice_file.write_text(txt, encoding="utf-8")
221
245
 
@@ -225,7 +249,8 @@ except ImportError as _ex:
225
249
  command = f"{exe} -m ipdb {choice_file} " # pudb is not available on windows machines, use poor man's debugger instead.
226
250
  elif platform.system() in ["Linux", "Darwin"]:
227
251
  command = f"{exe} -m pudb {choice_file} " # TODO: functions not supported yet in debug mode.
228
- else: raise NotImplementedError(f"Platform {platform.system()} not supported.")
252
+ else:
253
+ raise NotImplementedError(f"Platform {platform.system()} not supported.")
229
254
  elif args.module:
230
255
  # both selected function and kwargs are mentioned in the made up script, therefore no need for fire module.
231
256
  command = f"{exe} {choice_file} "
@@ -249,15 +274,19 @@ except ImportError as _ex:
249
274
  # command = f"cd {choice_file.parent}\n{exe} {choice_file.name}\ncd {PathExtended.cwd()}"
250
275
  command = f"{exe} {choice_file} "
251
276
  if not args.cmd:
252
- if "ipdb" in command: command = f"pip install ipdb\n{command}"
253
- if "pudb" in command: command = f"pip install pudb\n{command}"
277
+ if "ipdb" in command:
278
+ command = f"pip install ipdb\n{command}"
279
+ if "pudb" in command:
280
+ command = f"pip install pudb\n{command}"
254
281
  command = f"{activate_ve_line}\n{command}"
255
282
  else:
256
283
  # CMD equivalent
257
- if "ipdb" in command: command = f"pip install ipdb & {command}"
258
- if "pudb" in command: command = f"pip install pudb & {command}"
284
+ if "ipdb" in command:
285
+ command = f"pip install ipdb & {command}"
286
+ if "pudb" in command:
287
+ command = f"pip install pudb & {command}"
259
288
  new_line = "\n"
260
- command = fr"""start cmd -Argument "/k {activate_ve_line.replace(".ps1", ".bat").replace(". ", "")} & {command.replace(new_line, " & ")} " """ # this works from powershell
289
+ command = rf"""start cmd -Argument "/k {activate_ve_line.replace(".ps1", ".bat").replace(". ", "")} & {command.replace(new_line, " & ")} " """ # this works from powershell
261
290
 
262
291
  if args.submit_to_cloud:
263
292
  command = f"""
@@ -281,6 +310,7 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
281
310
  for an_arg in range(args.Nprocess):
282
311
  tab_config[f"tab{an_arg}"] = (str(PathExtended.cwd()), f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={args.Nprocess}")
283
312
  from machineconfig.cluster.sessions_managers.zellij_local import run_zellij_layout
313
+
284
314
  run_zellij_layout(tab_config=tab_config, session_name=None)
285
315
  return None
286
316
 
@@ -293,6 +323,7 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
293
323
  from rich.panel import Panel
294
324
  from rich.console import Console
295
325
  from rich.syntax import Syntax
326
+
296
327
  console = Console()
297
328
 
298
329
  if args.zellij_tab is not None:
@@ -301,17 +332,22 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
301
332
  comman_path__.write_text(command, encoding="utf-8")
302
333
  console.print(Panel(Syntax(command, lexer="shell"), title=f"🔥 fire command @ {comman_path__}: "), style="bold red")
303
334
  import subprocess
335
+
304
336
  existing_tab_names = subprocess.run(["zellij", "action", "query-tab-names"], capture_output=True, text=True, check=True).stdout.splitlines()
305
337
  if args.zellij_tab in existing_tab_names:
306
338
  print(f"⚠️ Tab name `{args.zellij_tab}` already exists. Please choose a different name.")
307
339
  # args.zellij_tab = input("Please enter a new tab name: ")
308
340
  args.zellij_tab += f"_{randstr(3)}"
309
341
  from machineconfig.cluster.sessions_managers.zellij_local import run_command_in_zellij_tab
342
+
310
343
  command = run_command_in_zellij_tab(command=str(comman_path__), tab_name=args.zellij_tab, cwd=None)
311
- if args.watch: command = "watchexec --restart --exts py,sh,ps1 " + command
312
- if args.git_pull: command = f"\ngit -C {choice_file.parent} pull\n" + command
344
+ if args.watch:
345
+ command = "watchexec --restart --exts py,sh,ps1 " + command
346
+ if args.git_pull:
347
+ command = f"\ngit -C {choice_file.parent} pull\n" + command
313
348
  if args.PathExport:
314
- if platform.system() in ["Linux", "Darwin"]: export_line = f"""export PYTHONPATH="{repo_root}""" + """:${PYTHONPATH}" """
349
+ if platform.system() in ["Linux", "Darwin"]:
350
+ export_line = f"""export PYTHONPATH="{repo_root}""" + """:${PYTHONPATH}" """
315
351
  elif platform.system() == "Windows":
316
352
  # export_line = f"""set PYTHONPATH="{repo_root}""" + """:%PYTHONPATH%" """
317
353
  # powershell equivalent
@@ -327,8 +363,8 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
327
363
  command = command + "\nsleep 0.5"
328
364
  elif platform.system() == "Windows":
329
365
  # command = command + "timeout 0.5\n"
330
- #pwsh equivalent
331
- command ="$ErrorActionPreference = 'SilentlyContinue';\n" + command + "\nStart-Sleep -Seconds 0.5"
366
+ # pwsh equivalent
367
+ command = "$ErrorActionPreference = 'SilentlyContinue';\n" + command + "\nStart-Sleep -Seconds 0.5"
332
368
  else:
333
369
  raise NotImplementedError(f"Platform {platform.system()} not supported.")
334
370
  command = command + f"\n. {program_path}"
@@ -337,6 +373,6 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
337
373
  program_path.write_text(command, encoding="utf-8")
338
374
 
339
375
 
340
- if __name__ == '__main__':
376
+ if __name__ == "__main__":
341
377
  # options, func_args = parse_pyfile(file_path="C:/Users/aalsaf01/code/machineconfig/myresources/crocodile/core.py")
342
378
  main()
@@ -19,7 +19,7 @@ def main():
19
19
  ┃ 🚀 FTP File Transfer
20
20
  ┃ 📋 Starting transfer process...
21
21
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
22
- parser = argparse.ArgumentParser(description='FTP client')
22
+ parser = argparse.ArgumentParser(description="FTP client")
23
23
 
24
24
  parser.add_argument("source", help="source path (machine:path)")
25
25
  parser.add_argument("target", help="target path (machine:path)")
@@ -38,36 +38,44 @@ def main():
38
38
  source_parts = args.source.split(":")
39
39
  machine = source_parts[0]
40
40
  if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
41
- if args.target == ES: raise ValueError(f"""
41
+ if args.target == ES:
42
+ raise ValueError(f"""
42
43
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
43
44
  ┃ ❌ Configuration Error
44
45
  ┃ Cannot use expand symbol `{ES}` in both source and target
45
46
  ┃ This creates a cyclical inference dependency
46
47
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
47
- else: target = PathExtended(args.target).expanduser().absolute()
48
+ else:
49
+ target = PathExtended(args.target).expanduser().absolute()
48
50
  source = target.collapseuser().as_posix()
49
51
  else:
50
52
  source = ":".join(args.source.split(":")[1:])
51
- if args.target == ES: target = None
52
- else: target = PathExtended(args.target).expanduser().absolute().as_posix()
53
+ if args.target == ES:
54
+ target = None
55
+ else:
56
+ target = PathExtended(args.target).expanduser().absolute().as_posix()
53
57
 
54
58
  elif ":" in args.target and (args.target[1] != ":" if len(args.target) > 1 else True): # avoid the case of "C:/":
55
59
  source_is_remote = False
56
60
  target_parts = args.target.split(":")
57
61
  machine = target_parts[0]
58
62
  if len(target_parts) > 1 and target_parts[1] == ES:
59
- if args.source == ES: raise ValueError(f"""
63
+ if args.source == ES:
64
+ raise ValueError(f"""
60
65
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
66
  ┃ ❌ Configuration Error
62
67
  ┃ Cannot use expand symbol `{ES}` in both source and target
63
68
  ┃ This creates a cyclical inference dependency
64
69
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
65
- else: source = args.source
70
+ else:
71
+ source = args.source
66
72
  target = None
67
73
  else:
68
74
  target = ":".join(args.target.split(":")[1:])
69
- if args.source == ES: source = None
70
- else: source = PathExtended(args.source).expanduser().absolute()
75
+ if args.source == ES:
76
+ source = None
77
+ else:
78
+ source = PathExtended(args.source).expanduser().absolute()
71
79
 
72
80
  else:
73
81
  raise ValueError("""
@@ -80,8 +88,9 @@ def main():
80
88
  pprint({"source": str(source), "target": str(target), "machine": machine}, "CLI Resolution")
81
89
 
82
90
  from paramiko.ssh_exception import AuthenticationException # type: ignore
91
+
83
92
  try:
84
- ssh = SSH(rf'{machine}')
93
+ ssh = SSH(rf"{machine}")
85
94
  except AuthenticationException:
86
95
  print("""
87
96
  ┌────────────────────────────────────────────────────────────────
@@ -92,8 +101,9 @@ def main():
92
101
  │ This exception only handles password authentication
93
102
  └────────────────────────────────────────────────────────────────""")
94
103
  import getpass
104
+
95
105
  pwd = getpass.getpass()
96
- ssh = SSH(rf'{machine}', pwd=pwd)
106
+ ssh = SSH(rf"{machine}", pwd=pwd)
97
107
 
98
108
  if args.cloud:
99
109
  print("""
@@ -118,7 +128,7 @@ def main():
118
128
  │ 📥 Transfer Mode: Remote → Local
119
129
  │ Source: {source}
120
130
  │ Target: {target}
121
- │ Options: {'ZIP compression' if args.zipFirst else 'No compression'}, {'Recursive' if args.recursive else 'Non-recursive'}
131
+ │ Options: {"ZIP compression" if args.zipFirst else "No compression"}, {"Recursive" if args.recursive else "Non-recursive"}
122
132
  └────────────────────────────────────────────────────────────────""")
123
133
  received_file = ssh.copy_to_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
124
134
  else:
@@ -129,7 +139,7 @@ def main():
129
139
  │ 📤 Transfer Mode: Local → Remote
130
140
  │ Source: {source}
131
141
  │ Target: {target}
132
- │ Options: {'ZIP compression' if args.zipFirst else 'No compression'}, {'Recursive' if args.recursive else 'Non-recursive'}
142
+ │ Options: {"ZIP compression" if args.zipFirst else "No compression"}, {"Recursive" if args.recursive else "Non-recursive"}
133
143
  └────────────────────────────────────────────────────────────────""")
134
144
  received_file = ssh.copy_from_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
135
145
 
@@ -147,5 +157,5 @@ def main():
147
157
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
148
158
 
149
159
 
150
- if __name__ == '__main__':
160
+ if __name__ == "__main__":
151
161
  main()
@@ -3,12 +3,13 @@ from pathlib import Path
3
3
 
4
4
  def get_zellij_cmd(wd1: Path, wd2: Path) -> str:
5
5
  _ = wd1, wd2
6
- lines = [""" zellij action new-tab --name gitdiff""",
7
- """zellij action new-pane --direction down --name local --cwd ./data """,
8
- """zellij action write-chars "cd '{wd1}'; git status" """,
9
- """zellij action move-focus up; zellij action close-pane """,
10
- """zellij action new-pane --direction down --name remote --cwd code """,
11
- """zellij action write-chars "cd '{wd2}' """,
12
- """git status" """
6
+ lines = [
7
+ """ zellij action new-tab --name gitdiff""",
8
+ """zellij action new-pane --direction down --name local --cwd ./data """,
9
+ """zellij action write-chars "cd '{wd1}'; git status" """,
10
+ """zellij action move-focus up; zellij action close-pane """,
11
+ """zellij action new-pane --direction down --name remote --cwd code """,
12
+ """zellij action write-chars "cd '{wd2}' """,
13
+ """git status" """,
13
14
  ]
14
15
  return "; ".join(lines)