machineconfig 1.5__py3-none-any.whl → 1.8__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 +8 -5
- machineconfig/jobs/python/check_installations.py +173 -163
- machineconfig/jobs/python/checkout_version.py +117 -0
- machineconfig/jobs/python/create_bootable_media.py +14 -14
- machineconfig/jobs/python/create_zellij_template.py +59 -56
- machineconfig/jobs/python/python_cargo_build_share.py +50 -45
- machineconfig/jobs/python/python_ve_symlink.py +20 -18
- machineconfig/jobs/python/tasks.py +4 -0
- machineconfig/jobs/script_installer/azure_data_studio.py +22 -0
- machineconfig/jobs/script_installer/bypass_paywall.py +23 -0
- machineconfig/jobs/script_installer/code.py +34 -0
- machineconfig/jobs/script_installer/docker_desktop.py +41 -0
- machineconfig/jobs/script_installer/ngrok.py +29 -0
- machineconfig/jobs/{python_linux_installers → script_installer}/skim.py +21 -19
- machineconfig/jobs/script_installer/wezterm.py +34 -0
- machineconfig/profile/create.py +107 -200
- machineconfig/profile/shell.py +127 -0
- machineconfig/scripts/__init__.py +6 -6
- machineconfig/scripts/python/cloud_copy.py +93 -0
- machineconfig/scripts/python/cloud_manager.py +38 -0
- machineconfig/scripts/python/cloud_mount.py +115 -52
- machineconfig/scripts/python/cloud_repo_sync.py +154 -114
- machineconfig/scripts/python/cloud_sync.py +261 -79
- machineconfig/scripts/python/croshell.py +151 -0
- machineconfig/scripts/python/devops.py +119 -87
- machineconfig/scripts/python/devops_add_identity.py +27 -23
- machineconfig/scripts/python/devops_add_ssh_key.py +70 -55
- machineconfig/scripts/python/devops_backup_retrieve.py +52 -46
- machineconfig/scripts/python/devops_devapps_install.py +120 -91
- machineconfig/scripts/python/devops_update_repos.py +82 -68
- machineconfig/scripts/python/dotfile.py +42 -38
- machineconfig/scripts/python/fire_jobs.py +351 -98
- machineconfig/scripts/python/ftpx.py +82 -0
- machineconfig/scripts/python/mount_nfs.py +54 -3
- machineconfig/scripts/python/mount_nw_drive.py +31 -0
- machineconfig/scripts/python/mount_ssh.py +44 -20
- machineconfig/scripts/python/onetimeshare.py +60 -51
- machineconfig/scripts/python/pomodoro.py +41 -37
- machineconfig/scripts/python/repos.py +195 -128
- machineconfig/scripts/python/scheduler.py +52 -0
- machineconfig/scripts/python/snapshot.py +21 -21
- machineconfig/scripts/python/start_slidev.py +104 -0
- machineconfig/scripts/python/start_terminals.py +97 -0
- machineconfig/scripts/python/wifi_conn.py +90 -71
- machineconfig/scripts/python/{transfer_wsl_win.py → wsl_windows_transfer.py} +47 -39
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +44 -48
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +136 -130
- machineconfig/utils/installer.py +251 -0
- machineconfig/utils/procs.py +114 -64
- machineconfig/utils/scheduling.py +188 -0
- machineconfig/utils/utils.py +353 -249
- machineconfig/utils/ve.py +222 -0
- {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/METADATA +140 -110
- machineconfig-1.8.dist-info/RECORD +70 -0
- {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/WHEEL +1 -1
- machineconfig/jobs/python/python_linux_installers_all.py +0 -73
- machineconfig/jobs/python/python_ve_installer.py +0 -73
- machineconfig/jobs/python/python_windows_installers_all.py +0 -23
- machineconfig/jobs/python_generic_installers/archive/nvim.py +0 -15
- machineconfig/jobs/python_generic_installers/archive/strongbox.py +0 -32
- machineconfig/jobs/python_generic_installers/archive/vtm.py +0 -25
- machineconfig/jobs/python_generic_installers/broot.py +0 -39
- machineconfig/jobs/python_generic_installers/browsh.py +0 -25
- machineconfig/jobs/python_generic_installers/bw.py +0 -26
- machineconfig/jobs/python_generic_installers/chatgpt.py +0 -24
- machineconfig/jobs/python_generic_installers/cpufetch.py +0 -23
- machineconfig/jobs/python_generic_installers/delta.py +0 -59
- machineconfig/jobs/python_generic_installers/dev/__init__.py +0 -0
- machineconfig/jobs/python_generic_installers/dev/autogpt.py +0 -28
- machineconfig/jobs/python_generic_installers/dev/bw.py +0 -29
- machineconfig/jobs/python_generic_installers/dev/evcxr.py +0 -22
- machineconfig/jobs/python_generic_installers/dev/kondo.py +0 -21
- machineconfig/jobs/python_generic_installers/dev/lvim.py +0 -60
- machineconfig/jobs/python_generic_installers/dev/ngrok.py +0 -35
- machineconfig/jobs/python_generic_installers/dev/opencommit.py +0 -21
- machineconfig/jobs/python_generic_installers/dev/qrcp.py +0 -25
- machineconfig/jobs/python_generic_installers/dev/qrscan.py +0 -16
- machineconfig/jobs/python_generic_installers/dev/rust-analyzer.py +0 -24
- machineconfig/jobs/python_generic_installers/dev/termscp.py +0 -23
- machineconfig/jobs/python_generic_installers/dev/tldr.py +0 -25
- machineconfig/jobs/python_generic_installers/dev/tokei.py +0 -24
- machineconfig/jobs/python_generic_installers/diskonaut.py +0 -26
- machineconfig/jobs/python_generic_installers/dua.py +0 -21
- machineconfig/jobs/python_generic_installers/evcxr.py +0 -21
- machineconfig/jobs/python_generic_installers/gitui.py +0 -23
- machineconfig/jobs/python_generic_installers/gopass.py +0 -19
- machineconfig/jobs/python_generic_installers/helix.py +0 -45
- machineconfig/jobs/python_generic_installers/kondo.py +0 -20
- machineconfig/jobs/python_generic_installers/lf.py +0 -25
- machineconfig/jobs/python_generic_installers/lvim.py +0 -34
- machineconfig/jobs/python_generic_installers/mprocs.py +0 -20
- machineconfig/jobs/python_generic_installers/navi.py +0 -20
- machineconfig/jobs/python_generic_installers/ots.py +0 -26
- machineconfig/jobs/python_generic_installers/ouch.py +0 -25
- machineconfig/jobs/python_generic_installers/pomodoro.py +0 -19
- machineconfig/jobs/python_generic_installers/procs.py +0 -20
- machineconfig/jobs/python_generic_installers/qrcp.py +0 -22
- machineconfig/jobs/python_generic_installers/qrscan.py +0 -14
- machineconfig/jobs/python_generic_installers/rclone.py +0 -21
- machineconfig/jobs/python_generic_installers/rust-analyzer.py +0 -24
- machineconfig/jobs/python_generic_installers/tere.py +0 -26
- machineconfig/jobs/python_generic_installers/termscp.py +0 -23
- machineconfig/jobs/python_generic_installers/tldr.py +0 -24
- machineconfig/jobs/python_generic_installers/tokei.py +0 -21
- machineconfig/jobs/python_generic_installers/vtm.py +0 -26
- machineconfig/jobs/python_generic_installers/watchexec.py +0 -21
- machineconfig/jobs/python_linux_installers/archive/__init__.py +0 -0
- machineconfig/jobs/python_linux_installers/archive/ranger.py +0 -18
- machineconfig/jobs/python_linux_installers/bandwhich.py +0 -11
- machineconfig/jobs/python_linux_installers/bottom.py +0 -17
- machineconfig/jobs/python_linux_installers/btop.py +0 -17
- machineconfig/jobs/python_linux_installers/dev/bandwhich.py +0 -13
- machineconfig/jobs/python_linux_installers/dev/bytehound.py +0 -20
- machineconfig/jobs/python_linux_installers/dev/nnn.py +0 -21
- machineconfig/jobs/python_linux_installers/gotty.py +0 -16
- machineconfig/jobs/python_linux_installers/joshuto.py +0 -28
- machineconfig/jobs/python_linux_installers/mcfly.py +0 -12
- machineconfig/jobs/python_linux_installers/nnn.py +0 -18
- machineconfig/jobs/python_linux_installers/topgrade.py +0 -15
- machineconfig/jobs/python_linux_installers/viu.py +0 -19
- machineconfig/jobs/python_linux_installers/xplr.py +0 -22
- machineconfig/jobs/python_linux_installers/zellij.py +0 -31
- machineconfig/jobs/python_windows_installers/archive/ntop.py +0 -20
- machineconfig/jobs/python_windows_installers/bat.py +0 -16
- machineconfig/jobs/python_windows_installers/boxes.py +0 -19
- machineconfig/jobs/python_windows_installers/bypass_paywall.py +0 -18
- machineconfig/jobs/python_windows_installers/dev/bypass_paywall.py +0 -21
- machineconfig/jobs/python_windows_installers/dev/obs_background_removal_plugin.py +0 -20
- machineconfig/jobs/python_windows_installers/fd.py +0 -17
- machineconfig/jobs/python_windows_installers/fzf.py +0 -19
- machineconfig/jobs/python_windows_installers/obs_background_removal_plugin.py +0 -19
- machineconfig/jobs/python_windows_installers/rg.py +0 -15
- machineconfig/jobs/python_windows_installers/ugrep.py +0 -14
- machineconfig/jobs/python_windows_installers/zoomit.py +0 -20
- machineconfig/jobs/python_windows_installers/zoxide.py +0 -20
- machineconfig/profile/fix_shell_profiles.py +0 -8
- machineconfig/scripts/python/archive/__init__.py +0 -0
- machineconfig/scripts/python/archive/bu_gdrive_rx.py +0 -41
- machineconfig/scripts/python/archive/bu_gdrive_sx.py +0 -40
- machineconfig/scripts/python/archive/bu_onedrive_rx.py +0 -59
- machineconfig/scripts/python/archive/bu_onedrive_sx.py +0 -45
- machineconfig/scripts/python/chatgpt.py +0 -28
- machineconfig/scripts/python/choose_ohmybash_theme.py +0 -25
- machineconfig/scripts/python/choose_ohmyposh_theme.py +0 -40
- machineconfig/scripts/python/cloud_rx.py +0 -42
- machineconfig/scripts/python/cloud_sx.py +0 -40
- machineconfig/scripts/python/ftprx.py +0 -37
- machineconfig/scripts/python/ftpsx.py +0 -36
- machineconfig/scripts/python/im2text.py +0 -15
- machineconfig/scripts/python/tmate_conn.py +0 -28
- machineconfig/scripts/python/tmate_start.py +0 -31
- machineconfig/utils/to_exe.py +0 -7
- machineconfig-1.5.dist-info/RECORD +0 -147
- /machineconfig/jobs/{python_generic_installers/archive → script_installer}/__init__.py +0 -0
- {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/top_level.txt +0 -0
|
@@ -1,98 +1,351 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
fire
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
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
|
+
# https://github.com/pallets/click combine with fire. Consider
|
|
10
|
+
# https://github.com/ceccopierangiolieugenio/pyTermTk for display_options build TUI
|
|
11
|
+
# https://github.com/chriskiehl/Gooey build commandline interface
|
|
12
|
+
from crocodile.file_management import P, install_n_import
|
|
13
|
+
from crocodile.core import Display, randstr
|
|
14
|
+
import inspect
|
|
15
|
+
import platform
|
|
16
|
+
import os
|
|
17
|
+
from typing import Callable, Any, Optional
|
|
18
|
+
import argparse
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> None:
|
|
22
|
+
parser = argparse.ArgumentParser()
|
|
23
|
+
parser.add_argument("path", nargs='?', type=str, help="The directory containing the jobs", default=".")
|
|
24
|
+
parser.add_argument("function", nargs='?', type=str, help="Fuction to run", default=None)
|
|
25
|
+
# 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")
|
|
30
|
+
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")
|
|
33
|
+
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")
|
|
38
|
+
parser.add_argument("--kw", nargs="*", default=None, help="keyword arguments to pass to the function in the form of k1 v1 k2 v2 ...")
|
|
39
|
+
|
|
40
|
+
args = parser.parse_args()
|
|
41
|
+
if args.kw is not None:
|
|
42
|
+
assert len(args.kw) % 2 == 0, f"args.kw must be a list of even length. Got {len(args.kw)}"
|
|
43
|
+
kwargs = dict(zip(args.kw[::2], args.kw[1::2]))
|
|
44
|
+
# print(f"kwargs = {kwargs}")
|
|
45
|
+
else:
|
|
46
|
+
kwargs = {}
|
|
47
|
+
|
|
48
|
+
path_obj = sanitize_path(P(args.path))
|
|
49
|
+
if not path_obj.exists():
|
|
50
|
+
print("This pathway")
|
|
51
|
+
path_obj = match_file_name(args.path)
|
|
52
|
+
print(path_obj)
|
|
53
|
+
else:
|
|
54
|
+
print("This directory")
|
|
55
|
+
print(path_obj)
|
|
56
|
+
|
|
57
|
+
if path_obj.is_dir():
|
|
58
|
+
print(f"Seaching recursively for all python file in directory `{path_obj}`")
|
|
59
|
+
py_files = path_obj.search(pattern="*.py", not_in=["__init__.py"], r=True).list
|
|
60
|
+
ps_files = path_obj.search(pattern="*.ps1", r=True).list
|
|
61
|
+
sh_files = path_obj.search(pattern="*.sh", r=True).list
|
|
62
|
+
files = py_files + ps_files + sh_files
|
|
63
|
+
|
|
64
|
+
choice_file = choose_one_option(options=files, fzf=True)
|
|
65
|
+
choice_file = P(choice_file)
|
|
66
|
+
else:
|
|
67
|
+
choice_file = path_obj
|
|
68
|
+
|
|
69
|
+
if choice_file.suffix in [".ps1", ".sh"]:
|
|
70
|
+
PROGRAM_PATH.write_text(f". {choice_file}")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
if args.choose_function or args.submit_to_cloud:
|
|
74
|
+
|
|
75
|
+
options, func_args = parse_pyfile(file_path=str(choice_file))
|
|
76
|
+
choice_function_tmp = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
|
|
77
|
+
assert isinstance(choice_function_tmp, str), f"choice_function must be a string. Got {type(choice_function_tmp)}"
|
|
78
|
+
choice_index = options.index(choice_function_tmp)
|
|
79
|
+
choice_function: Optional[str] = choice_function_tmp.split(' -- ')[0]
|
|
80
|
+
choice_function_args = func_args[choice_index]
|
|
81
|
+
|
|
82
|
+
if choice_function == "RUN AS MAIN": choice_function = None
|
|
83
|
+
if len(choice_function_args) > 0 and len(kwargs) == 0:
|
|
84
|
+
for item in choice_function_args:
|
|
85
|
+
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
|
|
87
|
+
|
|
88
|
+
if args.ve == "":
|
|
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
|
+
args.ve = get_ve_profile(choice_file)
|
|
91
|
+
|
|
92
|
+
if args.streamlit: exe = "streamlit run --server.address 0.0.0.0 "
|
|
93
|
+
elif args.interactive is False: exe = "python"
|
|
94
|
+
elif args.jupyter: exe = "jupyter-lab"
|
|
95
|
+
else:
|
|
96
|
+
from machineconfig.utils.ve import get_ipython_profile
|
|
97
|
+
exe = f"ipython -i --no-banner --profile {get_ipython_profile(choice_file)} "
|
|
98
|
+
|
|
99
|
+
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.
|
|
100
|
+
txt: str = f"""
|
|
101
|
+
try:
|
|
102
|
+
{get_import_module_code(str(choice_file))}
|
|
103
|
+
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.")
|
|
106
|
+
import sys
|
|
107
|
+
sys.path.append(r'{P(choice_file).parent}')
|
|
108
|
+
from {P(choice_file).stem} import *
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
if choice_function is not None:
|
|
112
|
+
txt = txt + f"""
|
|
113
|
+
{choice_function}({('**' + str(kwargs)) if kwargs else ''})
|
|
114
|
+
"""
|
|
115
|
+
txt = f"""
|
|
116
|
+
from machineconfig.utils.utils import print_code
|
|
117
|
+
print_code(code=r'''{txt}''', lexer='python', desc='Import Script')
|
|
118
|
+
""" + txt
|
|
119
|
+
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
|
+
|
|
121
|
+
# determining basic command structure: putting together exe & choice_file & choice_function & pdb
|
|
122
|
+
if args.debug:
|
|
123
|
+
if platform.system() == "Windows":
|
|
124
|
+
command = f"{exe} -m ipdb {choice_file} " # pudb is not available on windows machines, use poor man's debugger instead.
|
|
125
|
+
elif platform.system() in ["Linux", "Darwin"]:
|
|
126
|
+
command = f"{exe} -m pudb {choice_file} " # TODO: functions not supported yet in debug mode.
|
|
127
|
+
else: raise NotImplementedError(f"Platform {platform.system()} not supported.")
|
|
128
|
+
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.
|
|
129
|
+
# https://google.github.io/python-fire/guide/
|
|
130
|
+
# https://github.com/google/python-fire/blob/master/docs/guide.md#argument-parsing
|
|
131
|
+
if not kwargs: # empty dict
|
|
132
|
+
kwargs_str = ''
|
|
133
|
+
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__ + "}'"
|
|
143
|
+
command = f"{exe} -m fire {choice_file} {choice_function} {kwargs_str}"
|
|
144
|
+
# else:
|
|
145
|
+
# print(f"{kwargs=}")
|
|
146
|
+
# print(f"{choice_function_args=}")
|
|
147
|
+
# if choice_function != "RUN AS MAIN":
|
|
148
|
+
# kgs1, _ = interactively_run_function(module[choice_function])
|
|
149
|
+
# " ".join([f"--{k} {v}" for k, v in kgs1.items()])
|
|
150
|
+
else:
|
|
151
|
+
if not args.streamlit: command = f"{exe} {choice_file} "
|
|
152
|
+
else:
|
|
153
|
+
if not args.cmd:
|
|
154
|
+
# 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()}"
|
|
156
|
+
else:
|
|
157
|
+
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()}"
|
|
159
|
+
|
|
160
|
+
# this installs in ve env, which is not execution env
|
|
161
|
+
# if "ipdb" in command: install_n_import("ipdb")
|
|
162
|
+
# if "pudb" in command: install_n_import("pudb")
|
|
163
|
+
|
|
164
|
+
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}"
|
|
168
|
+
else:
|
|
169
|
+
# CMD equivalent
|
|
170
|
+
if "ipdb" in command: command = f"pip install ipdb & {command}"
|
|
171
|
+
if "pudb" in command: command = f"pip install pudb & {command}"
|
|
172
|
+
command = fr"""start cmd -Argument "/k %USERPROFILE%\venvs\{args.ve}\Scripts\activate.bat & {command} " """ # this works from powershell
|
|
173
|
+
# 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)
|
|
174
|
+
|
|
175
|
+
if args.submit_to_cloud:
|
|
176
|
+
command = f"""
|
|
177
|
+
. activate_ve {args.ve}
|
|
178
|
+
python -m crocodile.cluster.templates.cli_click --file {choice_file} """
|
|
179
|
+
if choice_function is not None: command += f"--function {choice_function} "
|
|
180
|
+
try: install_n_import("clipboard").copy(command)
|
|
181
|
+
except Exception as ex: print(f"Failed to copy command to clipboard. {ex}")
|
|
182
|
+
|
|
183
|
+
if args.loop:
|
|
184
|
+
command = command + f"\n" + f". {PROGRAM_PATH}"
|
|
185
|
+
|
|
186
|
+
# 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 "
|
|
187
|
+
print(f"🔥 command:\n{command}\n\n")
|
|
188
|
+
# if platform.system() == "Linux":
|
|
189
|
+
# command = "timeout 1s aafire -driver slang\nclear\n" + command
|
|
190
|
+
PROGRAM_PATH.write_text(command)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def parse_pyfile(file_path: str):
|
|
194
|
+
print(f"Loading {file_path} ...")
|
|
195
|
+
from typing import NamedTuple
|
|
196
|
+
args_spec = NamedTuple("args_spec", [("name", str), ("type", str), ("default", Optional[str])])
|
|
197
|
+
func_args: list[list[args_spec]] = [[]] # this firt prepopulated dict is for the option 'RUN AS MAIN' which has no args
|
|
198
|
+
|
|
199
|
+
import ast
|
|
200
|
+
parsed_ast = ast.parse(P(file_path).read_text(encoding='utf-8'))
|
|
201
|
+
functions = [
|
|
202
|
+
node
|
|
203
|
+
for node in ast.walk(parsed_ast)
|
|
204
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
205
|
+
]
|
|
206
|
+
module__doc__ = ast.get_docstring(parsed_ast)
|
|
207
|
+
main_option = f"RUN AS MAIN -- {Display.get_repr(module__doc__, limit=150) if module__doc__ is not None else 'NoDocs'}"
|
|
208
|
+
options = [main_option]
|
|
209
|
+
for function in functions:
|
|
210
|
+
if function.name.startswith('__') and function.name.endswith('__'): continue
|
|
211
|
+
if any(arg.arg == 'self' for arg in function.args.args): continue
|
|
212
|
+
doc_string_tmp: str | None = ast.get_docstring(function)
|
|
213
|
+
if doc_string_tmp is None: doc_string = "NoDocs"
|
|
214
|
+
else: doc_string = doc_string_tmp.replace('\n', ' ')
|
|
215
|
+
options.append(f"{function.name} -- {', '.join([arg.arg for arg in function.args.args])} -- {doc_string}")
|
|
216
|
+
tmp = []
|
|
217
|
+
for idx, arg in enumerate(function.args.args):
|
|
218
|
+
if arg.annotation is not None:
|
|
219
|
+
try: type_ = arg.annotation.__dict__['id']
|
|
220
|
+
except KeyError as ke:
|
|
221
|
+
# type_ = arg.annotation.__name__
|
|
222
|
+
# print(f"Failed to get type for {arg.annotation}. {ke}")
|
|
223
|
+
# Struct(get_attrs(arg.annotation)).print(as_yaml=True)
|
|
224
|
+
type_ = "Any" # e.g. a callable object
|
|
225
|
+
_ = ke
|
|
226
|
+
# raise ke
|
|
227
|
+
else: type_ = "Any"
|
|
228
|
+
default_tmp = function.args.defaults[idx] if idx < len(function.args.defaults) else None
|
|
229
|
+
if default_tmp is None: default = None
|
|
230
|
+
else:
|
|
231
|
+
if hasattr(default_tmp, "__dict__"): default = default_tmp.__dict__.get("value", None)
|
|
232
|
+
else: default = None
|
|
233
|
+
tmp.append(args_spec(name=arg.arg, type=type_, default=default))
|
|
234
|
+
func_args.append(tmp)
|
|
235
|
+
return options, func_args
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def get_attrs(obj: Any):
|
|
239
|
+
if hasattr(obj, '__dict__'):
|
|
240
|
+
res = {}
|
|
241
|
+
for k, v in obj.__dict__.items():
|
|
242
|
+
res[k] = get_attrs(v)
|
|
243
|
+
return res
|
|
244
|
+
return obj
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def interactively_run_function(func: Callable[[Any], Any]):
|
|
248
|
+
sig = inspect.signature(func)
|
|
249
|
+
params = list(sig.parameters.values())
|
|
250
|
+
args = []
|
|
251
|
+
kwargs = {}
|
|
252
|
+
for param in params:
|
|
253
|
+
if param.annotation is not inspect.Parameter.empty: hint = f" ({param.annotation.__name__})"
|
|
254
|
+
else: hint = ""
|
|
255
|
+
if param.default is not inspect.Parameter.empty:
|
|
256
|
+
default = param.default
|
|
257
|
+
value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) (default = {default}) : ")
|
|
258
|
+
if value == "": value = default
|
|
259
|
+
else: value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) : ")
|
|
260
|
+
try:
|
|
261
|
+
if param.annotation is not inspect.Parameter.empty: value = param.annotation(value)
|
|
262
|
+
except (TypeError, ValueError) as err:
|
|
263
|
+
raise ValueError(f"Invalid input: {value} is not of type {param.annotation}") from err
|
|
264
|
+
if param.kind == inspect.Parameter.KEYWORD_ONLY: kwargs[param.name] = value
|
|
265
|
+
else: args.append((param.name, value))
|
|
266
|
+
args_to_kwargs = dict(args)
|
|
267
|
+
return args_to_kwargs, kwargs
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def run_on_remote(func_file: str, args: argparse.Namespace):
|
|
271
|
+
host = choose_ssh_host(multi=False)
|
|
272
|
+
assert isinstance(host, str), f"host must be a string. Got {type(host)}"
|
|
273
|
+
from crocodile.cluster.remote_machine import RemoteMachine, RemoteMachineConfig
|
|
274
|
+
config = RemoteMachineConfig(copy_repo=True, update_repo=False, update_essential_repos=True,
|
|
275
|
+
notify_upon_completion=True, ssh_params=dict(host=host),
|
|
276
|
+
# to_email=None, email_config_name='enaut',
|
|
277
|
+
data=[],
|
|
278
|
+
ipython=False, interactive=args.interactive, pdb=False, pudb=args.debug, wrap_in_try_except=False,
|
|
279
|
+
transfer_method="sftp")
|
|
280
|
+
m = RemoteMachine(func=func_file, func_kwargs=None, config=config)
|
|
281
|
+
m.run()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def find_root_path(start_path: str):
|
|
285
|
+
root_files = ['setup.py', 'pyproject.toml', '.git']
|
|
286
|
+
path = start_path
|
|
287
|
+
while path != '/':
|
|
288
|
+
for root_file in root_files:
|
|
289
|
+
if os.path.exists(os.path.join(path, root_file)):
|
|
290
|
+
return path
|
|
291
|
+
path = os.path.dirname(path)
|
|
292
|
+
return None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def get_import_module_code(module_path: str):
|
|
296
|
+
root_path = find_root_path(module_path)
|
|
297
|
+
if root_path is None: # just make a desperate attempt to import it
|
|
298
|
+
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.", "")
|
|
303
|
+
return f"from {module_name} import *"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def get_jupyter_notebook(python_code: str):
|
|
307
|
+
template = """
|
|
308
|
+
{
|
|
309
|
+
"cells": [
|
|
310
|
+
{
|
|
311
|
+
"cell_type": "code",
|
|
312
|
+
"execution_count": 1,
|
|
313
|
+
"id": "7412902a-3074-475b-9820-71b82e670a2a",
|
|
314
|
+
"metadata": {},
|
|
315
|
+
"outputs": [],
|
|
316
|
+
"source": [
|
|
317
|
+
"\n",
|
|
318
|
+
"import math"
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
],
|
|
322
|
+
"metadata": {
|
|
323
|
+
"kernelspec": {
|
|
324
|
+
"display_name": "Python 3 (ipykernel)",
|
|
325
|
+
"language": "python",
|
|
326
|
+
"name": "python3"
|
|
327
|
+
},
|
|
328
|
+
"language_info": {
|
|
329
|
+
"codemirror_mode": {
|
|
330
|
+
"name": "ipython",
|
|
331
|
+
"version": 3
|
|
332
|
+
},
|
|
333
|
+
"file_extension": ".py",
|
|
334
|
+
"mimetype": "text/x-python",
|
|
335
|
+
"name": "python",
|
|
336
|
+
"nbconvert_exporter": "python",
|
|
337
|
+
"pygments_lexer": "ipython3",
|
|
338
|
+
"version": "3.11.7"
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"nbformat": 4,
|
|
342
|
+
"nbformat_minor": 5
|
|
343
|
+
}
|
|
344
|
+
"""
|
|
345
|
+
template.replace('"import math"', python_code)
|
|
346
|
+
return template
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if __name__ == '__main__':
|
|
350
|
+
# options, func_args = parse_pyfile(file_path="C:/Users/aalsaf01/code/crocodile/myresources/crocodile/core.py")
|
|
351
|
+
main()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
"""Sx & Rx
|
|
3
|
+
|
|
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
|
+
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
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
from crocodile.meta import SSH, P, Struct
|
|
10
|
+
from machineconfig.scripts.python.cloud_sync import ES
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
parser = argparse.ArgumentParser(description='FTP client')
|
|
15
|
+
|
|
16
|
+
parser.add_argument("source", help=f"source path (machine:path)")
|
|
17
|
+
parser.add_argument("target", help="target path (machine:path)")
|
|
18
|
+
|
|
19
|
+
# FLAGS
|
|
20
|
+
parser.add_argument("--recursive", "-r", help="Send recursively.", action="store_true") # default is False
|
|
21
|
+
parser.add_argument("--zipFirst", "-z", help="Zip before sending.", action="store_true") # default is False
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
|
|
25
|
+
if ":" in args.source and (args.source[1] != ":" if len(args.source) > 1 else True): # avoid the case of "C:/":
|
|
26
|
+
source_is_remote = True
|
|
27
|
+
|
|
28
|
+
# calculating source:
|
|
29
|
+
source_parts = args.source.split(":")
|
|
30
|
+
machine = source_parts[0]
|
|
31
|
+
if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
|
|
32
|
+
if args.target == ES: raise ValueError(f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised.")
|
|
33
|
+
else: target = P(args.target).expanduser().absolute()
|
|
34
|
+
source = target.collapseuser().as_posix()
|
|
35
|
+
else:
|
|
36
|
+
source = ":".join(args.source.split(":")[1:])
|
|
37
|
+
if args.target == ES: target = None
|
|
38
|
+
else: target = P(args.target).expanduser().absolute().as_posix()
|
|
39
|
+
|
|
40
|
+
elif ":" in args.target and (args.target[1] != ":" if len(args.target) > 1 else True): # avoid the case of "C:/":
|
|
41
|
+
source_is_remote = False
|
|
42
|
+
target_parts = args.target.split(":")
|
|
43
|
+
machine = target_parts[0]
|
|
44
|
+
if len(target_parts) > 1 and target_parts[1] == ES:
|
|
45
|
+
if args.source == ES: raise ValueError(f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised.")
|
|
46
|
+
else: source = args.source
|
|
47
|
+
target = None
|
|
48
|
+
else:
|
|
49
|
+
target = ":".join(args.target.split(":")[1:])
|
|
50
|
+
if args.source == ES: source = None
|
|
51
|
+
else: source = P(args.source).expanduser().absolute()
|
|
52
|
+
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError("Either source or target must be a remote path (i.e. machine:path)")
|
|
55
|
+
|
|
56
|
+
Struct({"source": str(source), "target": str(target), "machine": machine}).print(as_config=True, title="CLI Resolution")
|
|
57
|
+
from paramiko.ssh_exception import AuthenticationException # type: ignore
|
|
58
|
+
try:
|
|
59
|
+
ssh = SSH(rf'{machine}')
|
|
60
|
+
except AuthenticationException:
|
|
61
|
+
print("Authentication failed, trying manually:")
|
|
62
|
+
print(f"Caution: Ensure that username is passed appropriately as this exception only handles password.")
|
|
63
|
+
import getpass
|
|
64
|
+
pwd = getpass.getpass()
|
|
65
|
+
ssh = SSH(rf'{machine}', pwd=pwd)
|
|
66
|
+
|
|
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)
|
|
71
|
+
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)
|
|
75
|
+
# ssh.print_summary()
|
|
76
|
+
# if P(args.file).is_dir(): print(f"Use: cd {repr(P(args.file).expanduser())}")
|
|
77
|
+
if source_is_remote and isinstance(received_file, P):
|
|
78
|
+
print(f"Received: {repr(received_file.parent), repr(received_file)}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == '__main__':
|
|
82
|
+
main()
|
|
@@ -1,3 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
|
|
2
|
+
"""NFS mounting script
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from crocodile.file_management import P
|
|
7
|
+
from crocodile.meta import SSH, Terminal
|
|
8
|
+
from crocodile.core import List as L
|
|
9
|
+
from machineconfig.utils.utils import display_options, PROGRAM_PATH, choose_ssh_host
|
|
10
|
+
import platform
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
print(f"Mounting NFS Share ... ")
|
|
15
|
+
share_info = input("share path? (e.g. machine:~/data/share_nfs) [press enter for interactive choice] = ")
|
|
16
|
+
if share_info == "":
|
|
17
|
+
tmp = choose_ssh_host(multi=False)
|
|
18
|
+
assert isinstance(tmp, str)
|
|
19
|
+
ssh = SSH(tmp)
|
|
20
|
+
default = f"{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_nfs"
|
|
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)
|
|
22
|
+
assert isinstance(share_info, str), f"share_info must be a string. Got {type(share_info)}"
|
|
23
|
+
remote_server = share_info.split(":")[0]
|
|
24
|
+
share_path = share_info.split(":")[1]
|
|
25
|
+
|
|
26
|
+
if platform.system() == "Linux":
|
|
27
|
+
mount_path_1 = P(share_path)
|
|
28
|
+
print(remote_server)
|
|
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:])
|
|
31
|
+
else: mount_path_3 = mount_path_2
|
|
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)
|
|
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()
|
|
35
|
+
PROGRAM_PATH.write_text(f"""
|
|
36
|
+
share_info={share_info}
|
|
37
|
+
remote_server={remote_server}
|
|
38
|
+
share_path={share_path}
|
|
39
|
+
local_mount_point={local_mount_point}
|
|
40
|
+
""")
|
|
41
|
+
elif platform.system() == "Windows":
|
|
42
|
+
print(Terminal().run("Get-PSDrive -PSProvider 'FileSystem'", shell="powershell").op)
|
|
43
|
+
driver_letter = input(r"Choose driver letter (e.g. Z:\) (avoid the ones already used) : ") or "Z:\\"
|
|
44
|
+
PROGRAM_PATH.write_text(f"""
|
|
45
|
+
$server = "{remote_server}"
|
|
46
|
+
$sharePath = "{share_path}"
|
|
47
|
+
$driveLetter = "{driver_letter}"
|
|
48
|
+
""")
|
|
49
|
+
# P.home().joinpath(f"data/mount_nfs/{remote_server}").symlink_to(target="") # can't be created until the mount is finished
|
|
50
|
+
print(PROGRAM_PATH.read_text())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == '__main__':
|
|
54
|
+
main()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from machineconfig.utils.utils import PROGRAM_PATH
|
|
4
|
+
from crocodile.file_management import P
|
|
5
|
+
import platform
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
print(f"Welcome to the WindowsNetworkDrive Mounting Wizard")
|
|
10
|
+
drive_location = input("Enter the network drive location (ex: //192.168.1.100/Share): ")
|
|
11
|
+
machine_name = drive_location.split("//")[1].split("/")[0]
|
|
12
|
+
mount_point = input(f"Enter the mount point directory (ex: /mnt/network) [default: ~/data/mount_nw/{machine_name}]: ")
|
|
13
|
+
if mount_point == "": mount_point = P.home().joinpath(fr"data/mount_nw/{machine_name}")
|
|
14
|
+
else: mount_point = P(mount_point).expanduser()
|
|
15
|
+
|
|
16
|
+
username = input(f"Enter the username: ")
|
|
17
|
+
password = input(f"Enter the password: ")
|
|
18
|
+
if platform.system() == "Linux":
|
|
19
|
+
PROGRAM_PATH.write_text(f"""
|
|
20
|
+
drive_location='{drive_location}'
|
|
21
|
+
mount_point='{mount_point}'
|
|
22
|
+
username='{username}'
|
|
23
|
+
password='{password}'
|
|
24
|
+
|
|
25
|
+
""")
|
|
26
|
+
elif platform.system() == "Windows":
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == '__main__':
|
|
31
|
+
main()
|