machineconfig 1.8__py3-none-any.whl → 1.9__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 (37) hide show
  1. machineconfig/__init__.py +4 -2
  2. machineconfig/jobs/python/check_installations.py +1 -1
  3. machineconfig/jobs/python_custom_installers/azuredatastudio.py +36 -0
  4. machineconfig/jobs/{script_installer → python_custom_installers}/bypass_paywall.py +8 -1
  5. machineconfig/jobs/{script_installer → python_custom_installers}/docker_desktop.py +14 -3
  6. machineconfig/jobs/python_custom_installers/gh.py +53 -0
  7. machineconfig/jobs/python_custom_installers/goes.py +35 -0
  8. machineconfig/jobs/python_custom_installers/helix.py +43 -0
  9. machineconfig/jobs/python_custom_installers/lvim.py +48 -0
  10. machineconfig/jobs/{script_installer → python_custom_installers}/ngrok.py +11 -1
  11. machineconfig/jobs/python_custom_installers/nvim.py +48 -0
  12. machineconfig/jobs/{script_installer/code.py → python_custom_installers/vscode.py} +11 -0
  13. machineconfig/jobs/python_custom_installers/wezterm.py +41 -0
  14. machineconfig/profile/create.py +4 -1
  15. machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
  16. machineconfig/scripts/python/cloud_copy.py +5 -1
  17. machineconfig/scripts/python/cloud_mount.py +20 -10
  18. machineconfig/scripts/python/cloud_repo_sync.py +30 -22
  19. machineconfig/scripts/python/cloud_sync.py +4 -6
  20. machineconfig/scripts/python/croshell.py +17 -8
  21. machineconfig/scripts/python/devops_devapps_install.py +12 -6
  22. machineconfig/scripts/python/fire_jobs.py +92 -53
  23. machineconfig/scripts/python/ftpx.py +17 -7
  24. machineconfig/scripts/python/repos.py +5 -1
  25. machineconfig/scripts/python/start_terminals.py +1 -1
  26. machineconfig/utils/installer.py +98 -30
  27. machineconfig/utils/utils.py +6 -4
  28. machineconfig/utils/ve.py +37 -16
  29. machineconfig-1.9.dist-info/LICENSE +201 -0
  30. {machineconfig-1.8.dist-info → machineconfig-1.9.dist-info}/METADATA +155 -140
  31. {machineconfig-1.8.dist-info → machineconfig-1.9.dist-info}/RECORD +34 -28
  32. {machineconfig-1.8.dist-info → machineconfig-1.9.dist-info}/WHEEL +1 -1
  33. machineconfig/jobs/script_installer/azure_data_studio.py +0 -22
  34. machineconfig/jobs/script_installer/skim.py +0 -21
  35. machineconfig/jobs/script_installer/wezterm.py +0 -34
  36. /machineconfig/jobs/{script_installer → python_custom_installers}/__init__.py +0 -0
  37. {machineconfig-1.8.dist-info → machineconfig-1.9.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,7 @@ def get_wt_cmd(wd1: P, wd2: P) -> str:
20
20
  ]
21
21
  return " `; ".join(lines)
22
22
 
23
+
23
24
  def get_zellij_cmd(wd1: P, wd2: P) -> str:
24
25
  lines = [f""" zellij action new-tab --name gitdiff""",
25
26
  f"""zellij action new-pane --direction down --name local --cwd ./data """,
@@ -65,38 +66,38 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
65
66
  return ""
66
67
  else: cloud_resolved = cloud
67
68
  # repo_root = P(args.repo).expanduser().absolute()
68
- repo_root = P.cwd() if path is None else P(path).expanduser().absolute()
69
- repo_obj = install_n_import("git", "gitpython").Repo(repo_root, search_parent_directories=True)
70
- repo_root = P(repo_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
69
+ repo_local_root = P.cwd() if path is None else P(path).expanduser().absolute()
70
+ repo_local_obj = install_n_import("git", "gitpython").Repo(repo_local_root, search_parent_directories=True)
71
+ repo_local_root = P(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
71
72
  CONFIG_PATH.joinpath("remote").create()
72
- repo_sync_root = CONFIG_PATH.joinpath("remote", repo_root.rel2home()) # .delete(sure=True)
73
+ repo_remote_root = CONFIG_PATH.joinpath("remote", repo_local_root.rel2home()) # .delete(sure=True)
73
74
  try:
74
75
  print("\n", "=============================== Downloading Remote Repo ====================================")
75
- remote_path = repo_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
76
- repo_sync_root.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=pwd)
76
+ remote_path = repo_local_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
77
+ repo_remote_root.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=pwd)
77
78
  except AssertionError:
78
79
  print("Remote does not exist, creating it and exiting ... ")
79
- repo_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
80
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
80
81
  return ""
81
- repo_sync_obj = install_n_import("git", "gitpython").Repo(repo_sync_root)
82
- if repo_sync_obj.is_dirty():
83
- print("=" * 50, '\n', f"WRANING: the remote `{repo_sync_root}` is dirty, please commit or stash changes before proceeding.", '\n', "=" * 50)
82
+ repo_remote_obj = install_n_import("git", "gitpython").Repo(repo_remote_root)
83
+ if repo_remote_obj.is_dirty():
84
+ print("=" * 50, '\n', f"WRANING: the remote `{repo_remote_root}` is dirty, please commit or stash changes before proceeding.", '\n', "=" * 50)
84
85
 
85
86
  script = f"""
86
87
  echo ""
87
88
  echo "=============================== Committing Local Changes ==================================="
88
- cd {repo_root}
89
+ cd {repo_local_root}
89
90
  git status
90
91
  git add .
91
92
  git commit -am "{message}"
92
93
  echo ""
93
94
  echo ""
94
95
  echo "=============================== Pulling Latest From Remote ================================"
95
- cd {repo_root}
96
+ cd {repo_local_root}
96
97
  echo '-> Trying to removing originEnc remote from local repo if it exists.'
97
98
  git remote remove originEnc
98
99
  echo '-> Adding originEnc remote to local repo'
99
- git remote add originEnc {repo_sync_root}
100
+ git remote add originEnc {repo_remote_root}
100
101
  echo '-> Fetching originEnc remote.'
101
102
  git pull originEnc master
102
103
 
@@ -106,34 +107,41 @@ git pull originEnc master
106
107
 
107
108
  if res.is_successful(strict_err=True, strict_returcode=True):
108
109
  print("\n", "Pull succeeded, removing originEnc, the local copy of remote & pushing merged repo_root to remote ... ")
109
- repo_sync_root.delete(sure=True)
110
+ repo_remote_root.delete(sure=True)
110
111
  from git.remote import Remote
111
- Remote.remove(repo_obj, "originEnc")
112
+ Remote.remove(repo_local_obj, "originEnc")
112
113
  if push:
113
- repo_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
114
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
114
115
  else:
115
- print(f"Failed to pull, keeping local copy of remote at {repo_sync_root} ... ")
116
+ print(f"Failed to pull, keeping local copy of remote at {repo_remote_root} ... ")
116
117
 
117
118
  if push:
118
119
  if skip_confirmation: resp = "y"
119
- else: resp = input(f"Would you like to proceed syncing `{repo_root}` to `{cloud_resolved}` by pushing local changes to remote and deleting local copy of remote? y/[n] ") or "n"
120
+ else: resp = input(f"Would you like to proceed syncing `{repo_local_root}` to `{cloud_resolved}` by pushing local changes to remote and deleting local copy of remote? y/[n] ") or "n"
120
121
  else: resp = "n"
121
122
 
122
123
  if resp.lower() == "y":
123
- delete_remote_repo_copy_and_push_local(remote_repo=repo_sync_root.str, local_repo=repo_root.str, cloud=cloud_resolved)
124
+ delete_remote_repo_copy_and_push_local(remote_repo=repo_remote_root.str, local_repo=repo_local_root.str, cloud=cloud_resolved)
124
125
  else:
125
126
  program = f"""
126
127
  from machineconfig.scripts.python.cloud_repo_sync import delete_remote_repo_copy_and_push_local as func
127
- func(remote_repo=r'{repo_sync_root.str}', local_repo=r'{repo_root.str}', cloud=r'{cloud_resolved}')
128
+ func(remote_repo=r'{repo_remote_root.str}', local_repo=r'{repo_local_root.str}', cloud=r'{cloud_resolved}')
128
129
  """
129
130
  shell_file = get_shell_file_executing_python_script(python_script=program)
130
131
  print(f"When ready, use this snippet: \n. {shell_file}")
132
+ print(f"""
133
+ Or, if you want to delete local repo and replace with remote, run the following bash commands:
134
+
135
+ rm -rfd {repo_local_root}
136
+ mv {repo_remote_root} {repo_local_root}
137
+
138
+ """)
131
139
  if platform.system() == "Windows":
132
- program = get_wt_cmd(wd1=repo_root, wd2=repo_sync_root)
140
+ program = get_wt_cmd(wd1=repo_local_root, wd2=repo_remote_root)
133
141
  write_shell_script(program=program, execute=True)
134
142
  return None
135
143
  elif platform.system() == "Linux":
136
- program = get_zellij_cmd(wd1=repo_root, wd2=repo_sync_root)
144
+ program = get_zellij_cmd(wd1=repo_local_root, wd2=repo_remote_root)
137
145
  write_shell_script(program=program, execute=True)
138
146
  return None
139
147
  else: raise NotImplementedError(f"Platform {platform.system()} not implemented.")
@@ -5,14 +5,17 @@ TODO: use typer to make clis
5
5
  """
6
6
 
7
7
  from crocodile.file_management import P, Read, Struct
8
- from crocodile.core import install_n_import
8
+ # from crocodile.core import install_n_import
9
9
  from machineconfig.utils.utils import PROGRAM_PATH, DEFAULTS_PATH
10
10
  from machineconfig.scripts.python.cloud_mount import get_mprocs_mount_txt
11
11
  import argparse
12
12
  import os
13
13
  from typing import Optional
14
14
  # from dataclasses import dataclass
15
+ # install_n_import("pydantic")
15
16
  # from tap import Tap
17
+ from pydantic.dataclasses import dataclass
18
+ from pydantic import ConfigDict
16
19
 
17
20
 
18
21
  ES = "^" # chosen carefully to not mean anything on any shell. `$` was a bad choice.
@@ -32,11 +35,6 @@ class ArgsDefaults:
32
35
  pwd = None
33
36
 
34
37
 
35
- install_n_import("pydantic")
36
- from pydantic.dataclasses import dataclass # type: ignore # ruffle: ignore
37
- from pydantic import ConfigDict
38
-
39
-
40
38
  @dataclass(config=ConfigDict(extra="forbid", frozen=True))
41
39
  class Args():
42
40
  cloud: Optional[str] = None
@@ -8,7 +8,7 @@ import argparse
8
8
  # import subprocess
9
9
  # import platform
10
10
  from crocodile.file_management import P, randstr
11
- from machineconfig.utils.ve import get_ipython_profile, get_ve_profile
11
+ from machineconfig.utils.ve import get_ipython_profile, get_ve_profile, get_ve_name_and_ipython_profile
12
12
  from machineconfig.utils.utils import PROGRAM_PATH, display_options
13
13
 
14
14
 
@@ -34,8 +34,8 @@ def get_read_data_pycode(path: str):
34
34
  p = P(r\'{path}\').absolute()
35
35
  try:
36
36
  dat = p.readit()
37
- if type(dat) == Struct: dat.print(as_config=True, title=p.name)
38
- else: print(f"Succcesfully read the file {{p.name}}")
37
+ if isinstance(dat, dict): Struct(dat).print(as_config=True, title=p.name)
38
+ else: print(f"Successfully read the file {{p.name}}")
39
39
  except Exception as e:
40
40
  print(e)
41
41
 
@@ -93,17 +93,23 @@ def build_parser():
93
93
  options = P.cwd().search("*.py", r=True).apply(str).list
94
94
  file = display_options(msg="Choose a python file to run", options=options, fzf=True, multi=False, )
95
95
  assert isinstance(file, str)
96
- if profile is None: profile = profile = get_ipython_profile(P(file))
96
+ if profile is None:
97
+ profile = get_ipython_profile(P(file))
97
98
  program = P(file).read_text(encoding='utf-8')
98
99
 
99
100
  elif args.file != "":
100
101
  file = P(args.file.lstrip()).expanduser().absolute()
101
- if profile is None: profile = profile = get_ipython_profile(P(file))
102
+ if profile is None:
103
+ profile = get_ipython_profile(P(file))
102
104
  program = get_read_pyfile_pycode(file, as_module=args.module, cmd=args.cmd)
103
105
 
104
106
  elif args.read != "":
105
107
  file = P(str(args.read).lstrip()).expanduser().absolute()
106
- if profile is None: profile = profile = get_ipython_profile(P(file))
108
+ ve_name_from_file, ipy_profile_from_file = get_ve_name_and_ipython_profile(init_path=P(file))
109
+ # if profile is None:
110
+ # profile = get_ipython_profile(P(file))
111
+ if profile is None: profile = ipy_profile_from_file
112
+ if args.ve is None: args.ve = ve_name_from_file
107
113
  program = get_read_data_pycode(str(file))
108
114
 
109
115
  else: # just run croshell.py interactively
@@ -129,7 +135,9 @@ print_logo(logo="crocodile")
129
135
  total_program = preprogram + add_print_header_pycode(str(pyfile), title=title) + program
130
136
 
131
137
  pyfile.write_text(total_program, encoding='utf-8')
132
- if profile is None: profile = "default"
138
+ if profile is None:
139
+ profile = get_ipython_profile(P.cwd())
140
+ # profile = "default"
133
141
 
134
142
  ve = get_ve_profile(P(file)) if args.ve is None else str(args.ve)
135
143
 
@@ -140,7 +148,8 @@ print_logo(logo="crocodile")
140
148
  if interpreter == "ipython": final_program += f"{interactivity} --profile {profile} --no-banner"
141
149
  final_program += f" {str(pyfile)}"
142
150
  print(f"🔥 sourcing ... {pyfile}")
143
- PROGRAM_PATH.write_text(final_program)
151
+ # print(f"Running ... {final_program}")
152
+ PROGRAM_PATH.write_text(data=final_program)
144
153
 
145
154
  # if platform.system() == "Windows":
146
155
  # return subprocess.run([f"powershell", "-Command", res], shell=True, capture_output=False, text=True, check=True)
@@ -3,6 +3,7 @@
3
3
  """
4
4
 
5
5
  # import subprocess
6
+ from tqdm import tqdm
6
7
  from crocodile.core import List as L
7
8
  from machineconfig.utils.utils import LIBRARY_ROOT, choose_multiple_options
8
9
  from machineconfig.utils.installer import get_installers, Installer, install_all
@@ -15,11 +16,10 @@ WHICH: TypeAlias = Literal["AllEssentials", "EssentialsAndOthers", "SystemInstal
15
16
 
16
17
  def main(which: Optional[str] = None):
17
18
  sys = system()
18
- installers = get_installers(dev=False, system=sys) + get_installers(dev=True, system=sys)
19
+ installers = get_installers(dev=False, system=sys) # + get_installers(dev=True, system=sys)
19
20
  default = "AllEssentials"
20
- options = ["SystemInstallers", "OtherDevApps", "EssentialsAndOthers", "PrecheckedCloudInstaller"]
21
- options = [default] + options
22
- options = [x.get_description() for x in installers] + options
21
+ options = ["SystemInstallers", "OtherDevApps", "EssentialsAndOthers", "PrecheckedCloudInstaller", default]
22
+ options = [x.get_description() for x in tqdm(installers, desc="Checking installed programs")] + options
23
23
 
24
24
  if which is not None:
25
25
  return get_program(program_name=which, options=options, installers=list(installers))
@@ -41,7 +41,10 @@ def get_program(program_name: str, options: list[str], installers: list[Installe
41
41
  program = ""
42
42
  elif program_name == "SystemInstallers":
43
43
  if system() == "Windows": options_system = parse_apps_installer_windows(LIBRARY_ROOT.joinpath("setup_windows/apps.ps1").read_text())
44
- elif system() == "Linux": options_system = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text())
44
+ elif system() == "Linux":
45
+ options_system_1 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps_dev.sh").read_text())
46
+ options_system_2 = parse_apps_installer_linux(LIBRARY_ROOT.joinpath("setup_linux/apps.sh").read_text())
47
+ options_system = {**options_system_1, **options_system_2}
45
48
  else: raise NotImplementedError(f"System {system()} not supported")
46
49
  program_names = choose_multiple_options(msg="", options=sorted(list(options_system.keys())), header="CHOOSE DEV APP")
47
50
  program = ""
@@ -51,10 +54,12 @@ def get_program(program_name: str, options: list[str], installers: list[Installe
51
54
  program += "\n" + sub_program
52
55
  elif program_name == "OtherDevApps":
53
56
  installers = get_installers(dev=True, system=system())
54
- options__: list[str] = [x.get_description() for x in installers]
57
+ options__: list[str] = [x.get_description() for x in tqdm(installers, desc="Checking installed programs")]
55
58
  program_names = choose_multiple_options(msg="", options=sorted(options__) + ["all"], header="CHOOSE DEV APP")
56
59
  if "all" in program_names: program_names = options__
57
60
  program = ""
61
+ print(f"Installing:")
62
+ L(program_names).print()
58
63
  for name in program_names:
59
64
  try:
60
65
  idx = options__.index(name)
@@ -62,6 +67,7 @@ def get_program(program_name: str, options: list[str], installers: list[Installe
62
67
  print(f"{name=}")
63
68
  print(f"{options__=}")
64
69
  raise ve
70
+ print(f"Installing {name}")
65
71
  sub_program = installers[idx].install_robust(version=None) # finish the task
66
72
  elif program_name == "PrecheckedCloudInstaller":
67
73
  from machineconfig.jobs.python.check_installations import PrecheckedCloudInstaller
@@ -1,14 +1,15 @@
1
1
 
2
2
  """
3
3
  fire
4
- """
5
-
6
4
 
7
- from machineconfig.utils.utils import display_options, choose_one_option, PROGRAM_PATH, choose_ssh_host, match_file_name, sanitize_path
8
- # from crocodile.run import *
9
5
  # https://github.com/pallets/click combine with fire. Consider
10
6
  # https://github.com/ceccopierangiolieugenio/pyTermTk for display_options build TUI
11
7
  # https://github.com/chriskiehl/Gooey build commandline interface
8
+
9
+ """
10
+
11
+
12
+ from machineconfig.utils.utils import display_options, choose_one_option, PROGRAM_PATH, choose_ssh_host, match_file_name, sanitize_path
12
13
  from crocodile.file_management import P, install_n_import
13
14
  from crocodile.core import Display, randstr
14
15
  import inspect
@@ -20,21 +21,23 @@ import argparse
20
21
 
21
22
  def main() -> None:
22
23
  parser = argparse.ArgumentParser()
23
- parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
24
+ parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
24
25
  parser.add_argument("function", nargs='?', type=str, help="Fuction to run", default=None)
25
26
  # parser.add_argument("--function", "-f", type=str, help="The function to run", default="")
26
- parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
27
- parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
28
- parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
29
- parser.add_argument("--debug", "-d", action="store_true", help="debug")
27
+ parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
28
+ parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
29
+ parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
30
+ parser.add_argument("--debug", "-d", action="store_true", help="debug")
30
31
  parser.add_argument("--choose_function", "-c", action="store_true", help="debug")
31
- parser.add_argument("--loop", "-l", action="store_true", help="infinite recusion (runs again after completion)")
32
- parser.add_argument("--jupyter", "-j", action="store_true", help="open in a jupyter notebook")
32
+ parser.add_argument("--loop", "-l", action="store_true", help="infinite recusion (runs again after completion)")
33
+ parser.add_argument("--jupyter", "-j", action="store_true", help="open in a jupyter notebook")
33
34
  parser.add_argument("--submit_to_cloud", "-C", action="store_true", help="submit to cloud compute")
34
- parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
35
- parser.add_argument("--module", "-m", action="store_true", help="launch the main file")
36
- parser.add_argument("--streamlit", "-S", action="store_true", help="run as streamlit app")
37
- parser.add_argument("--history", "-H", action="store_true", help="choose from history")
35
+ parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
36
+ parser.add_argument("--module", "-m", action="store_true", help="launch the main file")
37
+ parser.add_argument("--streamlit", "-S", action="store_true", help="run as streamlit app")
38
+ parser.add_argument("--history", "-H", action="store_true", help="choose from history")
39
+ # parser.add_argument("--git_pull", "-g", action="store_true", help="Start by pulling the git repo")
40
+ parser.add_argument("--Nprocess", "-p", type=int, help="Number of processes to use", default=1)
38
41
  parser.add_argument("--kw", nargs="*", default=None, help="keyword arguments to pass to the function in the form of k1 v1 k2 v2 ...")
39
42
 
40
43
  args = parser.parse_args()
@@ -47,12 +50,9 @@ def main() -> None:
47
50
 
48
51
  path_obj = sanitize_path(P(args.path))
49
52
  if not path_obj.exists():
50
- print("This pathway")
51
53
  path_obj = match_file_name(args.path)
52
54
  print(path_obj)
53
- else:
54
- print("This directory")
55
- print(path_obj)
55
+ else: pass
56
56
 
57
57
  if path_obj.is_dir():
58
58
  print(f"Seaching recursively for all python file in directory `{path_obj}`")
@@ -68,10 +68,9 @@ def main() -> None:
68
68
 
69
69
  if choice_file.suffix in [".ps1", ".sh"]:
70
70
  PROGRAM_PATH.write_text(f". {choice_file}")
71
- return
71
+ return None
72
72
 
73
73
  if args.choose_function or args.submit_to_cloud:
74
-
75
74
  options, func_args = parse_pyfile(file_path=str(choice_file))
76
75
  choice_function_tmp = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
77
76
  assert isinstance(choice_function_tmp, str), f"choice_function must be a string. Got {type(choice_function_tmp)}"
@@ -83,13 +82,21 @@ def main() -> None:
83
82
  if len(choice_function_args) > 0 and len(kwargs) == 0:
84
83
  for item in choice_function_args:
85
84
  kwargs[item.name] = input(f"Please enter a value for argument `{item.name}` (type = {item.type}) (default = {item.default}) : ") or item.default
86
- else: choice_function = args.function
85
+ else:
86
+ choice_function = args.function
87
87
 
88
88
  if args.ve == "":
89
89
  from machineconfig.utils.ve import get_ve_profile # if file name is passed explicitly, then, user probably launched it from cwd different to repo root, so activate_ve can't infer ve from .ve_path, so we attempt to do that manually here
90
90
  args.ve = get_ve_profile(choice_file)
91
91
 
92
- if args.streamlit: exe = "streamlit run --server.address 0.0.0.0 "
92
+ if args.streamlit:
93
+ import socket
94
+ try: local_ip_v4 = socket.gethostbyname(socket.gethostname() + ".local") # without .local, in linux machines, '/etc/hosts' file content, you have an IP address mapping with '127.0.1.1' to your hostname
95
+ except Exception:
96
+ print(f"Warning: Could not get local_ip_v4. This is probably because you are running a WSL instance") # TODO find a way to get the local_ip_v4 in WSL
97
+ local_ip_v4 = socket.gethostbyname(socket.gethostname())
98
+ print(f"🚀 Streamlit app is running at: http://{local_ip_v4}:8501")
99
+ exe = "streamlit run --server.address 0.0.0.0 --server.headless true"
93
100
  elif args.interactive is False: exe = "python"
94
101
  elif args.jupyter: exe = "jupyter-lab"
95
102
  else:
@@ -97,12 +104,13 @@ def main() -> None:
97
104
  exe = f"ipython -i --no-banner --profile {get_ipython_profile(choice_file)} "
98
105
 
99
106
  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.
107
+ import_line = get_import_module_code(str(choice_file))
100
108
  txt: str = f"""
101
109
  try:
102
- {get_import_module_code(str(choice_file))}
110
+ {import_line}
103
111
  except (ImportError, ModuleNotFoundError) as ex:
104
- print(fr"Failed to import {choice_file} the proper way. {{ex}} ")
105
- print(fr"The way below is rather hacky and can cause issues in pickling.")
112
+ print(fr"Failed to import `{choice_file}` the proper way. {{ex}} ")
113
+ print(fr"Importing with an ad-hoc `$PATH` manipulation. DO NOT pickle any files in this session as there is no gaurantee of correct deserialization.")
106
114
  import sys
107
115
  sys.path.append(r'{P(choice_file).parent}')
108
116
  from {P(choice_file).stem} import *
@@ -110,11 +118,17 @@ except (ImportError, ModuleNotFoundError) as ex:
110
118
  """
111
119
  if choice_function is not None:
112
120
  txt = txt + f"""
113
- {choice_function}({('**' + str(kwargs)) if kwargs else ''})
121
+ res = {choice_function}({('**' + str(kwargs)) if kwargs else ''})
114
122
  """
115
123
  txt = f"""
116
- from machineconfig.utils.utils import print_code
117
- print_code(code=r'''{txt}''', lexer='python', desc='Import Script')
124
+ try:
125
+ from rich.panel import Panel
126
+ from rich.console import Console
127
+ from rich.syntax import Syntax
128
+ console = Console()
129
+ console.print(Panel(Syntax(code=r'''{txt}''', lexer='python'), title='Import Script'), style="bold red")
130
+ except ImportError as _ex:
131
+ print(r'''{txt}''')
118
132
  """ + txt
119
133
  choice_file = P.tmp().joinpath(f'tmp_scripts/python/{P(choice_file).parent.name}_{P(choice_file).stem}_{randstr()}.py').create(parents_only=True).write_text(txt)
120
134
 
@@ -131,15 +145,19 @@ print_code(code=r'''{txt}''', lexer='python', desc='Import Script')
131
145
  if not kwargs: # empty dict
132
146
  kwargs_str = ''
133
147
  else:
134
- tmp_list: list[str] = []
135
- for k, v in kwargs.items():
136
- if v is not None:
137
- item = f'"{k}": "{v}"'
138
- else:
139
- item = f'"{k}": None'
140
- tmp_list.append(item)
141
- tmp__ = ", ".join(tmp_list)
142
- kwargs_str = "'{" + tmp__ + "}'"
148
+ if len(kwargs) == 1:
149
+ kwargs_str = f""" --{list(kwargs.keys())[0]} {list(kwargs.values())[0]} """
150
+ else:
151
+ # print(f"len(kwargs) = {len(kwargs)}")
152
+ tmp_list: list[str] = []
153
+ for k, v in kwargs.items():
154
+ if v is not None:
155
+ item = f'"{k}": "{v}"'
156
+ else:
157
+ item = f'"{k}": None'
158
+ tmp_list.append(item)
159
+ tmp__ = ", ".join(tmp_list)
160
+ kwargs_str = "'{" + tmp__ + "}'"
143
161
  command = f"{exe} -m fire {choice_file} {choice_function} {kwargs_str}"
144
162
  # else:
145
163
  # print(f"{kwargs=}")
@@ -152,19 +170,22 @@ print_code(code=r'''{txt}''', lexer='python', desc='Import Script')
152
170
  else:
153
171
  if not args.cmd:
154
172
  # for .streamlit config to work, it needs to be in the current directory.
155
- command = f"cd {choice_file.parent}; {exe} {choice_file.name}; cd {P.cwd()}"
173
+ command = f"cd {choice_file.parent}\n\n{exe} {choice_file.name}\n\ncd {P.cwd()}"
156
174
  else:
157
175
  command = rf""" cd /d {choice_file.parent} & {exe} {choice_file.name} """
158
- # command = f"cd {choice_file.parent}; {exe} {choice_file.name}; cd {P.cwd()}"
176
+ # command = f"cd {choice_file.parent}\n\n{exe} {choice_file.name}\n\ncd {P.cwd()}"
159
177
 
160
178
  # this installs in ve env, which is not execution env
161
179
  # if "ipdb" in command: install_n_import("ipdb")
162
180
  # if "pudb" in command: install_n_import("pudb")
163
181
 
164
182
  if not args.cmd:
165
- if "ipdb" in command: command = f"pip install ipdb; {command}"
166
- if "pudb" in command: command = f"pip install pudb; {command}"
167
- command = f". activate_ve {args.ve}; {command}"
183
+ if "ipdb" in command: command = f"pip install ipdb\n\n{command}"
184
+ if "pudb" in command: command = f"pip install pudb\n\n{command}"
185
+ if platform.system() == "Windows":
186
+ command = f". activate_ve {args.ve}\n\n{command}"
187
+ else:
188
+ command = f". ~/scripts/activate_ve {args.ve}\n\n{command}"
168
189
  else:
169
190
  # CMD equivalent
170
191
  if "ipdb" in command: command = f"pip install ipdb & {command}"
@@ -183,7 +204,18 @@ python -m crocodile.cluster.templates.cli_click --file {choice_file} """
183
204
  if args.loop:
184
205
  command = command + f"\n" + f". {PROGRAM_PATH}"
185
206
 
207
+ if args.Nprocess > 1:
208
+ lines = [f""" zellij action new-tab --name nProcess{randstr(2)}"""]
209
+ command = command.replace(". activate_ve", ". ~/scripts/activate_ve")
210
+ for an_arg in range(args.Nprocess):
211
+ sub_command = f"{command} --idx={an_arg} --idx_max={args.Nprocess}"
212
+ sub_command_path = P.tmpfile(suffix=".sh").write_text(sub_command)
213
+ lines.append(f"""zellij action new-pane -- bash {sub_command_path} """)
214
+ lines.append("sleep 1") # python tends to freeze if you launch instances within 1 microsecond of each other
215
+ command = "\n".join(lines)
216
+
186
217
  # TODO: send this command to terminal history. In powershell & bash there is no way to do it with a command other than goiing to history file. In Mcfly there is a way but its linux only tool. # if platform.system() == "Windows": command = f" ({command}) | Add-History -PassThru "
218
+ # mcfly add --exit 0 command
187
219
  print(f"🔥 command:\n{command}\n\n")
188
220
  # if platform.system() == "Linux":
189
221
  # command = "timeout 1s aafire -driver slang\nclear\n" + command
@@ -235,11 +267,11 @@ def parse_pyfile(file_path: str):
235
267
  return options, func_args
236
268
 
237
269
 
238
- def get_attrs(obj: Any):
270
+ def get_attrs_recursively(obj: Any):
239
271
  if hasattr(obj, '__dict__'):
240
272
  res = {}
241
273
  for k, v in obj.__dict__.items():
242
- res[k] = get_attrs(v)
274
+ res[k] = get_attrs_recursively(v)
243
275
  return res
244
276
  return obj
245
277
 
@@ -281,25 +313,32 @@ def run_on_remote(func_file: str, args: argparse.Namespace):
281
313
  m.run()
282
314
 
283
315
 
284
- def find_root_path(start_path: str):
316
+ def find_repo_root_path(start_path: str) -> Optional[str]:
285
317
  root_files = ['setup.py', 'pyproject.toml', '.git']
286
- path = start_path
287
- while path != '/':
318
+ path: str = start_path
319
+ trials = 0
320
+ root_path = os.path.abspath(os.sep)
321
+ while path != root_path and trials < 20:
288
322
  for root_file in root_files:
289
323
  if os.path.exists(os.path.join(path, root_file)):
324
+ print(f"Found repo root path: {path}")
290
325
  return path
291
326
  path = os.path.dirname(path)
327
+ trials += 1
292
328
  return None
293
329
 
294
330
 
295
331
  def get_import_module_code(module_path: str):
296
- root_path = find_root_path(module_path)
332
+ root_path = find_repo_root_path(module_path)
297
333
  if root_path is None: # just make a desperate attempt to import it
298
334
  module_name = module_path.lstrip(os.sep).replace(os.sep, '.').replace('.py', '')
299
- return f"from {module_path} import *"
300
- relative_path = module_path.replace(root_path, '')
301
- module_name = relative_path.lstrip(os.sep).replace(os.sep, '.').replace('.py', '')
302
- module_name = module_name.replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("resources.", "").replace("source.", "")
335
+ else:
336
+ relative_path = module_path.replace(root_path, '')
337
+ module_name = relative_path.lstrip(os.sep).replace(os.sep, '.').replace('.py', '')
338
+ module_name = module_name.replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "").replace("src.", "").replace("resources.", "").replace("source.", "")
339
+ if any(char in module_name for char in "- :/\\"):
340
+ module_name = "IncorrectModuleName"
341
+ # TODO: use py_compile to check if the statement is valid code to avoid syntax errors that can't be caught.
303
342
  return f"from {module_name} import *"
304
343
 
305
344
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  TODO: add support for cases in which source or target has non 22 default port number and is defineda as user@host:port:path which makes 2 colons in the string.
5
5
  Currently, the only way to work around this is to predifine the host in ~/.ssh/config and use the alias in the source or target which is inconvenient when dealing with newly setup machines.
6
+
6
7
  """
7
8
 
8
9
  import argparse
@@ -19,6 +20,7 @@ def main():
19
20
  # FLAGS
20
21
  parser.add_argument("--recursive", "-r", help="Send recursively.", action="store_true") # default is False
21
22
  parser.add_argument("--zipFirst", "-z", help="Zip before sending.", action="store_true") # default is False
23
+ parser.add_argument("--cloud", "-c", help="Transfer through the cloud.", action="store_true") # default is False
22
24
 
23
25
  args = parser.parse_args()
24
26
 
@@ -54,6 +56,7 @@ def main():
54
56
  raise ValueError("Either source or target must be a remote path (i.e. machine:path)")
55
57
 
56
58
  Struct({"source": str(source), "target": str(target), "machine": machine}).print(as_config=True, title="CLI Resolution")
59
+
57
60
  from paramiko.ssh_exception import AuthenticationException # type: ignore
58
61
  try:
59
62
  ssh = SSH(rf'{machine}')
@@ -64,14 +67,21 @@ def main():
64
67
  pwd = getpass.getpass()
65
68
  ssh = SSH(rf'{machine}', pwd=pwd)
66
69
 
67
- if source_is_remote:
68
- assert source is not None, "source must be a remote path (i.e. machine:path)"
69
- print(f"Running: received_file = ssh.copy_to_here(source={source}, target={target}, z={args.zipFirst}, r={args.recursive})")
70
- received_file = ssh.copy_to_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
70
+ if args.cloud:
71
+ print("Uploading from remote to cloud ...")
72
+ ssh.run(f"cloud_copy {source} :^", desc="Uploading from remote to the cloud.").print()
73
+ print("Downloading from cloud to local ...")
74
+ ssh.run_locally(f"cloud_copy :^ {target}").print()
75
+ received_file = P(target) # type: ignore
71
76
  else:
72
- assert source is not None, "target must be a remote path (i.e. machine:path)"
73
- print(f"Running: received_file = ssh.copy_from_here(source={source}, target={target}, z={args.zipFirst}, r={args.recursive})")
74
- received_file = ssh.copy_from_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
77
+ if source_is_remote:
78
+ assert source is not None, "source must be a remote path (i.e. machine:path)"
79
+ print(f"Running: received_file = ssh.copy_to_here(source=r'{source}', target=r'{target}', z={args.zipFirst}, r={args.recursive})")
80
+ received_file = ssh.copy_to_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
81
+ else:
82
+ assert source is not None, "target must be a remote path (i.e. machine:path)"
83
+ print(f"Running: received_file = ssh.copy_from_here(source=r'{source}', target=r'{target}', z={args.zipFirst}, r={args.recursive})")
84
+ received_file = ssh.copy_from_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
75
85
  # ssh.print_summary()
76
86
  # if P(args.file).is_dir(): print(f"Use: cd {repr(P(args.file).expanduser())}")
77
87
  if source_is_remote and isinstance(received_file, P):
@@ -1,5 +1,9 @@
1
1
 
2
2
  """Repos
3
+
4
+ # TODO use gh api user --jq '.login' to get the username and use it to clone the repos.
5
+ in the event that username@github.com is not mentioned in the remote url.
6
+
3
7
  """
4
8
 
5
9
  from rich import print as pprint
@@ -168,7 +172,7 @@ def install_repos(specs_path: str, clone: bool = True, checkout_to_recorded_comm
168
172
  if preferred_remote is not None:
169
173
  if preferred_remote in repo["remotes"]:
170
174
  remote_name = preferred_remote
171
- remote_url: str = repo["remotes"][preferred_remote]
175
+ remote_url = repo["remotes"][preferred_remote]
172
176
  else:
173
177
  print(f"⚠️ `{preferred_remote=}` not found in {repo['remotes']}.")
174
178
  # preferred_remote = None
@@ -9,7 +9,7 @@ from typing import Literal
9
9
 
10
10
  COLOR_SCHEMES = ["Campbell", "Campbell Powershell", "Solarized Dark", "Ubuntu-ColorScheme", "Retro"]
11
11
  THEMES_ITER = cycle(COLOR_SCHEMES)
12
- INIT_COMMANDS = ["ls", "lf", "cpufetch", "neofetch", "btm"]
12
+ INIT_COMMANDS = ["ls", "lf", "cpufetch", "fastfetch", "btm"]
13
13
  INIT_COMMANDS_ITER = cycle(INIT_COMMANDS)
14
14
  SIZE_ITER = cycle([0.6, 0.4, 0.3])
15
15
  ORIENTATION = ["vertical", "horizontal"]