machineconfig 1.7__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.
- machineconfig/__init__.py +4 -2
- machineconfig/jobs/python/check_installations.py +38 -32
- machineconfig/jobs/python/create_bootable_media.py +4 -4
- machineconfig/jobs/python/create_zellij_template.py +3 -2
- machineconfig/jobs/python/python_cargo_build_share.py +14 -9
- machineconfig/jobs/python/python_ve_symlink.py +6 -6
- machineconfig/jobs/python_custom_installers/azuredatastudio.py +36 -0
- machineconfig/jobs/python_custom_installers/bypass_paywall.py +30 -0
- machineconfig/jobs/{python_linux_installers/dev → python_custom_installers}/docker_desktop.py +15 -4
- machineconfig/jobs/python_custom_installers/gh.py +53 -0
- machineconfig/jobs/python_custom_installers/goes.py +35 -0
- machineconfig/jobs/python_custom_installers/helix.py +43 -0
- machineconfig/jobs/python_custom_installers/lvim.py +48 -0
- machineconfig/jobs/python_custom_installers/ngrok.py +39 -0
- machineconfig/jobs/python_custom_installers/nvim.py +48 -0
- machineconfig/jobs/python_custom_installers/vscode.py +45 -0
- machineconfig/jobs/python_custom_installers/wezterm.py +41 -0
- machineconfig/profile/create.py +12 -7
- machineconfig/profile/shell.py +15 -13
- machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
- machineconfig/scripts/python/cloud_copy.py +18 -13
- machineconfig/scripts/python/cloud_mount.py +41 -15
- machineconfig/scripts/python/cloud_repo_sync.py +57 -31
- machineconfig/scripts/python/cloud_sync.py +13 -15
- machineconfig/scripts/python/croshell.py +19 -10
- machineconfig/scripts/python/devops.py +22 -6
- machineconfig/scripts/python/devops_add_identity.py +7 -6
- machineconfig/scripts/python/devops_add_ssh_key.py +10 -9
- machineconfig/scripts/python/devops_backup_retrieve.py +5 -5
- machineconfig/scripts/python/devops_devapps_install.py +24 -19
- machineconfig/scripts/python/devops_update_repos.py +5 -4
- machineconfig/scripts/python/dotfile.py +8 -4
- machineconfig/scripts/python/fire_jobs.py +165 -55
- machineconfig/scripts/python/ftpx.py +18 -8
- machineconfig/scripts/python/mount_nfs.py +13 -10
- machineconfig/scripts/python/mount_nw_drive.py +4 -3
- machineconfig/scripts/python/mount_ssh.py +8 -5
- machineconfig/scripts/python/repos.py +26 -21
- machineconfig/scripts/python/snapshot.py +2 -2
- machineconfig/scripts/python/start_slidev.py +104 -0
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +20 -34
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +11 -12
- machineconfig/utils/installer.py +177 -217
- machineconfig/utils/scheduling.py +1 -1
- machineconfig/utils/utils.py +107 -54
- machineconfig/utils/ve.py +120 -16
- machineconfig-1.9.dist-info/LICENSE +201 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/METADATA +155 -140
- machineconfig-1.9.dist-info/RECORD +76 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/WHEEL +1 -1
- machineconfig/jobs/python_generic_installers/archive/gopass.py +0 -18
- machineconfig/jobs/python_generic_installers/archive/nvim.py +0 -20
- machineconfig/jobs/python_generic_installers/archive/opencommit.py +0 -25
- machineconfig/jobs/python_generic_installers/archive/strongbox.py +0 -33
- machineconfig/jobs/python_generic_installers/dev/__init__.py +0 -0
- machineconfig/jobs/python_linux_installers/archive/__init__.py +0 -0
- machineconfig/jobs/python_linux_installers/archive/bandwhich.py +0 -14
- machineconfig/jobs/python_linux_installers/archive/ranger.py +0 -19
- machineconfig/jobs/python_linux_installers/dev/azure_data_studio.py +0 -21
- machineconfig/jobs/python_linux_installers/dev/bytehound.py +0 -20
- machineconfig/jobs/python_linux_installers/dev/nnn.py +0 -22
- machineconfig/jobs/python_windows_installers/archive/ntop.py +0 -21
- machineconfig/jobs/python_windows_installers/dev/bypass_paywall.py +0 -22
- machineconfig/jobs/python_windows_installers/dev/obs_background_removal_plugin.py +0 -22
- machineconfig/scripts/python/choose_ohmybash_theme.py +0 -31
- machineconfig/scripts/python/choose_ohmyposh_theme.py +0 -57
- machineconfig/utils/pandas_type.py +0 -37
- machineconfig/utils/to_exe.py +0 -7
- machineconfig-1.7.dist-info/RECORD +0 -81
- /machineconfig/jobs/{python_generic_installers/archive → python_custom_installers}/__init__.py +0 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"""Like yadm and dotter.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from crocodile.file_management import P
|
|
4
7
|
from machineconfig.profile.create import symlink
|
|
5
8
|
from machineconfig.utils.utils import LIBRARY_ROOT, REPO_ROOT
|
|
9
|
+
import argparse
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def main():
|
|
@@ -15,14 +19,14 @@ def main():
|
|
|
15
19
|
parser.add_argument("-d", "--dest", help=f"destination folder", default="")
|
|
16
20
|
|
|
17
21
|
args = parser.parse_args()
|
|
18
|
-
orig_path =
|
|
22
|
+
orig_path = P(args.file).expanduser().absolute()
|
|
19
23
|
if args.dest == "":
|
|
20
24
|
if "Local" in orig_path: junction = orig_path.split(at="Local", sep=-1)[1]
|
|
21
25
|
elif "Roaming" in orig_path: junction = orig_path.split(at="Roaming", sep=-1)[1]
|
|
22
26
|
elif ".config" in orig_path: junction = orig_path.split(at=".config", sep=-1)[1]
|
|
23
27
|
else: junction = orig_path.rel2home()
|
|
24
28
|
new_path = REPO_ROOT.joinpath(junction)
|
|
25
|
-
else: new_path =
|
|
29
|
+
else: new_path = P(args.dest).expanduser().absolute().create().joinpath(orig_path.name)
|
|
26
30
|
|
|
27
31
|
symlink(this=orig_path, to_this=new_path, prioritize_to_this=args.overwrite)
|
|
28
32
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
"""
|
|
3
3
|
fire
|
|
4
|
-
"""
|
|
5
4
|
|
|
6
|
-
import crocodile.toolbox as tb
|
|
7
|
-
from machineconfig.utils.utils import display_options, 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
|
|
13
|
+
from crocodile.file_management import P, install_n_import
|
|
14
|
+
from crocodile.core import Display, randstr
|
|
12
15
|
import inspect
|
|
13
16
|
import platform
|
|
14
17
|
import os
|
|
@@ -16,22 +19,25 @@ from typing import Callable, Any, Optional
|
|
|
16
19
|
import argparse
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
def main():
|
|
22
|
+
def main() -> None:
|
|
20
23
|
parser = argparse.ArgumentParser()
|
|
21
|
-
parser.add_argument("path",
|
|
24
|
+
parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
|
|
22
25
|
parser.add_argument("function", nargs='?', type=str, help="Fuction to run", default=None)
|
|
23
26
|
# parser.add_argument("--function", "-f", type=str, help="The function to run", default="")
|
|
24
|
-
parser.add_argument("--ve",
|
|
25
|
-
parser.add_argument("--cmd",
|
|
26
|
-
parser.add_argument("--interactive",
|
|
27
|
-
parser.add_argument("--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")
|
|
28
31
|
parser.add_argument("--choose_function", "-c", action="store_true", help="debug")
|
|
29
|
-
parser.add_argument("--loop",
|
|
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")
|
|
30
34
|
parser.add_argument("--submit_to_cloud", "-C", action="store_true", help="submit to cloud compute")
|
|
31
|
-
parser.add_argument("--remote",
|
|
32
|
-
parser.add_argument("--module",
|
|
33
|
-
parser.add_argument("--streamlit",
|
|
34
|
-
parser.add_argument("--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)
|
|
35
41
|
parser.add_argument("--kw", nargs="*", default=None, help="keyword arguments to pass to the function in the form of k1 v1 k2 v2 ...")
|
|
36
42
|
|
|
37
43
|
args = parser.parse_args()
|
|
@@ -42,8 +48,11 @@ def main():
|
|
|
42
48
|
else:
|
|
43
49
|
kwargs = {}
|
|
44
50
|
|
|
45
|
-
path_obj = sanitize_path(
|
|
46
|
-
if not path_obj.exists():
|
|
51
|
+
path_obj = sanitize_path(P(args.path))
|
|
52
|
+
if not path_obj.exists():
|
|
53
|
+
path_obj = match_file_name(args.path)
|
|
54
|
+
print(path_obj)
|
|
55
|
+
else: pass
|
|
47
56
|
|
|
48
57
|
if path_obj.is_dir():
|
|
49
58
|
print(f"Seaching recursively for all python file in directory `{path_obj}`")
|
|
@@ -52,18 +61,16 @@ def main():
|
|
|
52
61
|
sh_files = path_obj.search(pattern="*.sh", r=True).list
|
|
53
62
|
files = py_files + ps_files + sh_files
|
|
54
63
|
|
|
55
|
-
choice_file =
|
|
56
|
-
|
|
57
|
-
choice_file = tb.P(choice_file)
|
|
64
|
+
choice_file = choose_one_option(options=files, fzf=True)
|
|
65
|
+
choice_file = P(choice_file)
|
|
58
66
|
else:
|
|
59
67
|
choice_file = path_obj
|
|
60
68
|
|
|
61
69
|
if choice_file.suffix in [".ps1", ".sh"]:
|
|
62
70
|
PROGRAM_PATH.write_text(f". {choice_file}")
|
|
63
|
-
return
|
|
71
|
+
return None
|
|
64
72
|
|
|
65
73
|
if args.choose_function or args.submit_to_cloud:
|
|
66
|
-
|
|
67
74
|
options, func_args = parse_pyfile(file_path=str(choice_file))
|
|
68
75
|
choice_function_tmp = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
|
|
69
76
|
assert isinstance(choice_function_tmp, str), f"choice_function must be a string. Got {type(choice_function_tmp)}"
|
|
@@ -75,39 +82,55 @@ def main():
|
|
|
75
82
|
if len(choice_function_args) > 0 and len(kwargs) == 0:
|
|
76
83
|
for item in choice_function_args:
|
|
77
84
|
kwargs[item.name] = input(f"Please enter a value for argument `{item.name}` (type = {item.type}) (default = {item.default}) : ") or item.default
|
|
78
|
-
else:
|
|
85
|
+
else:
|
|
86
|
+
choice_function = args.function
|
|
79
87
|
|
|
80
88
|
if args.ve == "":
|
|
81
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
|
|
82
90
|
args.ve = get_ve_profile(choice_file)
|
|
83
91
|
|
|
84
|
-
if args.streamlit:
|
|
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"
|
|
85
100
|
elif args.interactive is False: exe = "python"
|
|
101
|
+
elif args.jupyter: exe = "jupyter-lab"
|
|
86
102
|
else:
|
|
87
103
|
from machineconfig.utils.ve import get_ipython_profile
|
|
88
104
|
exe = f"ipython -i --no-banner --profile {get_ipython_profile(choice_file)} "
|
|
89
105
|
|
|
90
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))
|
|
91
108
|
txt: str = f"""
|
|
92
109
|
try:
|
|
93
|
-
{
|
|
110
|
+
{import_line}
|
|
94
111
|
except (ImportError, ModuleNotFoundError) as ex:
|
|
95
|
-
print(fr"Failed to import {choice_file} the proper way. {{ex}} ")
|
|
96
|
-
print(fr"
|
|
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.")
|
|
97
114
|
import sys
|
|
98
|
-
sys.path.append(r'{
|
|
99
|
-
from {
|
|
115
|
+
sys.path.append(r'{P(choice_file).parent}')
|
|
116
|
+
from {P(choice_file).stem} import *
|
|
100
117
|
|
|
101
118
|
"""
|
|
102
119
|
if choice_function is not None:
|
|
103
120
|
txt = txt + f"""
|
|
104
|
-
{choice_function}({('**' + str(kwargs)) if kwargs else ''})
|
|
121
|
+
res = {choice_function}({('**' + str(kwargs)) if kwargs else ''})
|
|
105
122
|
"""
|
|
106
123
|
txt = f"""
|
|
107
|
-
|
|
108
|
-
|
|
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}''')
|
|
109
132
|
""" + txt
|
|
110
|
-
choice_file =
|
|
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)
|
|
111
134
|
|
|
112
135
|
# determining basic command structure: putting together exe & choice_file & choice_function & pdb
|
|
113
136
|
if args.debug:
|
|
@@ -118,8 +141,24 @@ print_code(code=r'''{txt}''', lexer='python', desc='Imported Script')
|
|
|
118
141
|
else: raise NotImplementedError(f"Platform {platform.system()} not supported.")
|
|
119
142
|
elif choice_function is not None and not args.module: # if args.module, then kwargs are handled in the impot script, no need to pass them in fire command.
|
|
120
143
|
# https://google.github.io/python-fire/guide/
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
# https://github.com/google/python-fire/blob/master/docs/guide.md#argument-parsing
|
|
145
|
+
if not kwargs: # empty dict
|
|
146
|
+
kwargs_str = ''
|
|
147
|
+
else:
|
|
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__ + "}'"
|
|
161
|
+
command = f"{exe} -m fire {choice_file} {choice_function} {kwargs_str}"
|
|
123
162
|
# else:
|
|
124
163
|
# print(f"{kwargs=}")
|
|
125
164
|
# print(f"{choice_function_args=}")
|
|
@@ -131,16 +170,26 @@ print_code(code=r'''{txt}''', lexer='python', desc='Imported Script')
|
|
|
131
170
|
else:
|
|
132
171
|
if not args.cmd:
|
|
133
172
|
# for .streamlit config to work, it needs to be in the current directory.
|
|
134
|
-
command = f"cd {choice_file.parent}
|
|
135
|
-
else:
|
|
136
|
-
|
|
173
|
+
command = f"cd {choice_file.parent}\n\n{exe} {choice_file.name}\n\ncd {P.cwd()}"
|
|
174
|
+
else:
|
|
175
|
+
command = rf""" cd /d {choice_file.parent} & {exe} {choice_file.name} """
|
|
176
|
+
# command = f"cd {choice_file.parent}\n\n{exe} {choice_file.name}\n\ncd {P.cwd()}"
|
|
137
177
|
|
|
138
|
-
|
|
139
|
-
if "
|
|
178
|
+
# this installs in ve env, which is not execution env
|
|
179
|
+
# if "ipdb" in command: install_n_import("ipdb")
|
|
180
|
+
# if "pudb" in command: install_n_import("pudb")
|
|
140
181
|
|
|
141
182
|
if not args.cmd:
|
|
142
|
-
command = f"
|
|
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}"
|
|
143
189
|
else:
|
|
190
|
+
# CMD equivalent
|
|
191
|
+
if "ipdb" in command: command = f"pip install ipdb & {command}"
|
|
192
|
+
if "pudb" in command: command = f"pip install pudb & {command}"
|
|
144
193
|
command = fr"""start cmd -Argument "/k %USERPROFILE%\venvs\{args.ve}\Scripts\activate.bat & {command} " """ # this works from powershell
|
|
145
194
|
# this works from cmd # command = fr""" start cmd /k "%USERPROFILE%\venvs\{args.ve}\Scripts\activate.bat & {command} " """ # because start in cmd is different from start in powershell (in powershell it is short for Start-Process)
|
|
146
195
|
|
|
@@ -149,13 +198,24 @@ print_code(code=r'''{txt}''', lexer='python', desc='Imported Script')
|
|
|
149
198
|
. activate_ve {args.ve}
|
|
150
199
|
python -m crocodile.cluster.templates.cli_click --file {choice_file} """
|
|
151
200
|
if choice_function is not None: command += f"--function {choice_function} "
|
|
152
|
-
try:
|
|
201
|
+
try: install_n_import("clipboard").copy(command)
|
|
153
202
|
except Exception as ex: print(f"Failed to copy command to clipboard. {ex}")
|
|
154
203
|
|
|
155
204
|
if args.loop:
|
|
156
205
|
command = command + f"\n" + f". {PROGRAM_PATH}"
|
|
157
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
|
+
|
|
158
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
|
|
159
219
|
print(f"🔥 command:\n{command}\n\n")
|
|
160
220
|
# if platform.system() == "Linux":
|
|
161
221
|
# command = "timeout 1s aafire -driver slang\nclear\n" + command
|
|
@@ -169,14 +229,14 @@ def parse_pyfile(file_path: str):
|
|
|
169
229
|
func_args: list[list[args_spec]] = [[]] # this firt prepopulated dict is for the option 'RUN AS MAIN' which has no args
|
|
170
230
|
|
|
171
231
|
import ast
|
|
172
|
-
parsed_ast = ast.parse(
|
|
232
|
+
parsed_ast = ast.parse(P(file_path).read_text(encoding='utf-8'))
|
|
173
233
|
functions = [
|
|
174
234
|
node
|
|
175
235
|
for node in ast.walk(parsed_ast)
|
|
176
236
|
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
177
237
|
]
|
|
178
238
|
module__doc__ = ast.get_docstring(parsed_ast)
|
|
179
|
-
main_option = f"RUN AS MAIN -- {
|
|
239
|
+
main_option = f"RUN AS MAIN -- {Display.get_repr(module__doc__, limit=150) if module__doc__ is not None else 'NoDocs'}"
|
|
180
240
|
options = [main_option]
|
|
181
241
|
for function in functions:
|
|
182
242
|
if function.name.startswith('__') and function.name.endswith('__'): continue
|
|
@@ -192,7 +252,7 @@ def parse_pyfile(file_path: str):
|
|
|
192
252
|
except KeyError as ke:
|
|
193
253
|
# type_ = arg.annotation.__name__
|
|
194
254
|
# print(f"Failed to get type for {arg.annotation}. {ke}")
|
|
195
|
-
#
|
|
255
|
+
# Struct(get_attrs(arg.annotation)).print(as_yaml=True)
|
|
196
256
|
type_ = "Any" # e.g. a callable object
|
|
197
257
|
_ = ke
|
|
198
258
|
# raise ke
|
|
@@ -207,11 +267,11 @@ def parse_pyfile(file_path: str):
|
|
|
207
267
|
return options, func_args
|
|
208
268
|
|
|
209
269
|
|
|
210
|
-
def
|
|
270
|
+
def get_attrs_recursively(obj: Any):
|
|
211
271
|
if hasattr(obj, '__dict__'):
|
|
212
272
|
res = {}
|
|
213
273
|
for k, v in obj.__dict__.items():
|
|
214
|
-
res[k] =
|
|
274
|
+
res[k] = get_attrs_recursively(v)
|
|
215
275
|
return res
|
|
216
276
|
return obj
|
|
217
277
|
|
|
@@ -253,28 +313,78 @@ def run_on_remote(func_file: str, args: argparse.Namespace):
|
|
|
253
313
|
m.run()
|
|
254
314
|
|
|
255
315
|
|
|
256
|
-
def
|
|
316
|
+
def find_repo_root_path(start_path: str) -> Optional[str]:
|
|
257
317
|
root_files = ['setup.py', 'pyproject.toml', '.git']
|
|
258
|
-
path = start_path
|
|
259
|
-
|
|
318
|
+
path: str = start_path
|
|
319
|
+
trials = 0
|
|
320
|
+
root_path = os.path.abspath(os.sep)
|
|
321
|
+
while path != root_path and trials < 20:
|
|
260
322
|
for root_file in root_files:
|
|
261
323
|
if os.path.exists(os.path.join(path, root_file)):
|
|
324
|
+
print(f"Found repo root path: {path}")
|
|
262
325
|
return path
|
|
263
326
|
path = os.path.dirname(path)
|
|
327
|
+
trials += 1
|
|
264
328
|
return None
|
|
265
329
|
|
|
266
330
|
|
|
267
331
|
def get_import_module_code(module_path: str):
|
|
268
|
-
root_path =
|
|
332
|
+
root_path = find_repo_root_path(module_path)
|
|
269
333
|
if root_path is None: # just make a desperate attempt to import it
|
|
270
334
|
module_name = module_path.lstrip(os.sep).replace(os.sep, '.').replace('.py', '')
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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.
|
|
275
342
|
return f"from {module_name} import *"
|
|
276
343
|
|
|
277
344
|
|
|
345
|
+
def get_jupyter_notebook(python_code: str):
|
|
346
|
+
template = """
|
|
347
|
+
{
|
|
348
|
+
"cells": [
|
|
349
|
+
{
|
|
350
|
+
"cell_type": "code",
|
|
351
|
+
"execution_count": 1,
|
|
352
|
+
"id": "7412902a-3074-475b-9820-71b82e670a2a",
|
|
353
|
+
"metadata": {},
|
|
354
|
+
"outputs": [],
|
|
355
|
+
"source": [
|
|
356
|
+
"\n",
|
|
357
|
+
"import math"
|
|
358
|
+
]
|
|
359
|
+
}
|
|
360
|
+
],
|
|
361
|
+
"metadata": {
|
|
362
|
+
"kernelspec": {
|
|
363
|
+
"display_name": "Python 3 (ipykernel)",
|
|
364
|
+
"language": "python",
|
|
365
|
+
"name": "python3"
|
|
366
|
+
},
|
|
367
|
+
"language_info": {
|
|
368
|
+
"codemirror_mode": {
|
|
369
|
+
"name": "ipython",
|
|
370
|
+
"version": 3
|
|
371
|
+
},
|
|
372
|
+
"file_extension": ".py",
|
|
373
|
+
"mimetype": "text/x-python",
|
|
374
|
+
"name": "python",
|
|
375
|
+
"nbconvert_exporter": "python",
|
|
376
|
+
"pygments_lexer": "ipython3",
|
|
377
|
+
"version": "3.11.7"
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"nbformat": 4,
|
|
381
|
+
"nbformat_minor": 5
|
|
382
|
+
}
|
|
383
|
+
"""
|
|
384
|
+
template.replace('"import math"', python_code)
|
|
385
|
+
return template
|
|
386
|
+
|
|
387
|
+
|
|
278
388
|
if __name__ == '__main__':
|
|
279
389
|
# options, func_args = parse_pyfile(file_path="C:/Users/aalsaf01/code/crocodile/myresources/crocodile/core.py")
|
|
280
390
|
main()
|
|
@@ -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,17 +67,24 @@ def main():
|
|
|
64
67
|
pwd = getpass.getpass()
|
|
65
68
|
ssh = SSH(rf'{machine}', pwd=pwd)
|
|
66
69
|
|
|
67
|
-
if
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
if isinstance(received_file, P):
|
|
87
|
+
if source_is_remote and isinstance(received_file, P):
|
|
78
88
|
print(f"Received: {repr(received_file.parent), repr(received_file)}")
|
|
79
89
|
|
|
80
90
|
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
"""NFS mounting script
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from crocodile.file_management import P
|
|
7
|
+
from crocodile.meta import SSH, Terminal
|
|
8
|
+
from crocodile.core import List as L
|
|
6
9
|
from machineconfig.utils.utils import display_options, PROGRAM_PATH, choose_ssh_host
|
|
7
10
|
import platform
|
|
8
11
|
|
|
@@ -13,22 +16,22 @@ def main():
|
|
|
13
16
|
if share_info == "":
|
|
14
17
|
tmp = choose_ssh_host(multi=False)
|
|
15
18
|
assert isinstance(tmp, str)
|
|
16
|
-
ssh =
|
|
19
|
+
ssh = SSH(tmp)
|
|
17
20
|
default = f"{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_nfs"
|
|
18
|
-
share_info = display_options("Choose a share path: ", options=
|
|
21
|
+
share_info = display_options("Choose a share path: ", options=L(ssh.run("cat /etc/exports").op.split("\n")).filter(lambda x: not x.startswith("#")).apply(lambda x: f"{ssh.hostname}:{x.split(' ')[0]}").list + [default], default=default)
|
|
19
22
|
assert isinstance(share_info, str), f"share_info must be a string. Got {type(share_info)}"
|
|
20
23
|
remote_server = share_info.split(":")[0]
|
|
21
24
|
share_path = share_info.split(":")[1]
|
|
22
25
|
|
|
23
26
|
if platform.system() == "Linux":
|
|
24
|
-
mount_path_1 =
|
|
27
|
+
mount_path_1 = P(share_path)
|
|
25
28
|
print(remote_server)
|
|
26
|
-
mount_path_2 =
|
|
27
|
-
if str(mount_path_1).startswith("/home"): mount_path_3 =
|
|
29
|
+
mount_path_2 = P.home().joinpath(f"data/mount_nfs/{remote_server}")
|
|
30
|
+
if str(mount_path_1).startswith("/home"): mount_path_3 = P.home().joinpath(*mount_path_1.parts[3:])
|
|
28
31
|
else: mount_path_3 = mount_path_2
|
|
29
32
|
local_mount_point = display_options(msg="choose mount path OR input custom one", options=[mount_path_1, mount_path_2, mount_path_3], default=mount_path_2, custom_input=True)
|
|
30
|
-
assert isinstance(local_mount_point,
|
|
31
|
-
local_mount_point =
|
|
33
|
+
assert isinstance(local_mount_point, P), f"local_mount_point must be a pathlib.Path. Got {type(local_mount_point)}"
|
|
34
|
+
local_mount_point = P(local_mount_point).expanduser()
|
|
32
35
|
PROGRAM_PATH.write_text(f"""
|
|
33
36
|
share_info={share_info}
|
|
34
37
|
remote_server={remote_server}
|
|
@@ -36,14 +39,14 @@ share_path={share_path}
|
|
|
36
39
|
local_mount_point={local_mount_point}
|
|
37
40
|
""")
|
|
38
41
|
elif platform.system() == "Windows":
|
|
39
|
-
print(
|
|
42
|
+
print(Terminal().run("Get-PSDrive -PSProvider 'FileSystem'", shell="powershell").op)
|
|
40
43
|
driver_letter = input(r"Choose driver letter (e.g. Z:\) (avoid the ones already used) : ") or "Z:\\"
|
|
41
44
|
PROGRAM_PATH.write_text(f"""
|
|
42
45
|
$server = "{remote_server}"
|
|
43
46
|
$sharePath = "{share_path}"
|
|
44
47
|
$driveLetter = "{driver_letter}"
|
|
45
48
|
""")
|
|
46
|
-
#
|
|
49
|
+
# P.home().joinpath(f"data/mount_nfs/{remote_server}").symlink_to(target="") # can't be created until the mount is finished
|
|
47
50
|
print(PROGRAM_PATH.read_text())
|
|
48
51
|
|
|
49
52
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
from machineconfig.utils.utils import PROGRAM_PATH
|
|
4
|
+
from crocodile.file_management import P
|
|
4
5
|
import platform
|
|
5
6
|
|
|
6
7
|
|
|
@@ -9,8 +10,8 @@ def main():
|
|
|
9
10
|
drive_location = input("Enter the network drive location (ex: //192.168.1.100/Share): ")
|
|
10
11
|
machine_name = drive_location.split("//")[1].split("/")[0]
|
|
11
12
|
mount_point = input(f"Enter the mount point directory (ex: /mnt/network) [default: ~/data/mount_nw/{machine_name}]: ")
|
|
12
|
-
if mount_point == "": mount_point =
|
|
13
|
-
else: mount_point =
|
|
13
|
+
if mount_point == "": mount_point = P.home().joinpath(fr"data/mount_nw/{machine_name}")
|
|
14
|
+
else: mount_point = P(mount_point).expanduser()
|
|
14
15
|
|
|
15
16
|
username = input(f"Enter the username: ")
|
|
16
17
|
password = input(f"Enter the password: ")
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
"""Mount a remote SSHFS share on a local directory
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
from platform import system
|
|
7
|
+
from crocodile.meta import SSH, Terminal
|
|
8
|
+
from crocodile.file_management import P
|
|
9
|
+
|
|
7
10
|
from machineconfig.utils.utils import PROGRAM_PATH, choose_ssh_host
|
|
8
11
|
|
|
9
12
|
|
|
@@ -14,15 +17,15 @@ def main():
|
|
|
14
17
|
if share_info == "":
|
|
15
18
|
tmp = choose_ssh_host(multi=False)
|
|
16
19
|
assert isinstance(tmp, str)
|
|
17
|
-
ssh =
|
|
20
|
+
ssh = SSH(host=tmp)
|
|
18
21
|
share_info = f"{ssh.username}@{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_ssh"
|
|
19
22
|
else:
|
|
20
|
-
ssh =
|
|
21
|
-
print(
|
|
23
|
+
ssh = SSH(share_info.split(":")[0])
|
|
24
|
+
print(Terminal().run("net use", shell="powershell").op)
|
|
22
25
|
driver_letter = input(r"Choose driver letter (e.g. Z:\) (avoid the ones already used) : ") or "Z:\\"
|
|
23
26
|
|
|
24
27
|
mount_point = input(f"Enter the mount point directory (ex: /mnt/network) [default: ~/data/mount_ssh/{ssh.hostname}]: ")
|
|
25
|
-
if mount_point == "": mount_point =
|
|
28
|
+
if mount_point == "": mount_point = P.home().joinpath(fr"data/mount_ssh/{ssh.hostname}")
|
|
26
29
|
|
|
27
30
|
if system() == "Linux":
|
|
28
31
|
txt = fr"""
|