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.

Files changed (155) hide show
  1. machineconfig/__init__.py +8 -5
  2. machineconfig/jobs/python/check_installations.py +173 -163
  3. machineconfig/jobs/python/checkout_version.py +117 -0
  4. machineconfig/jobs/python/create_bootable_media.py +14 -14
  5. machineconfig/jobs/python/create_zellij_template.py +59 -56
  6. machineconfig/jobs/python/python_cargo_build_share.py +50 -45
  7. machineconfig/jobs/python/python_ve_symlink.py +20 -18
  8. machineconfig/jobs/python/tasks.py +4 -0
  9. machineconfig/jobs/script_installer/azure_data_studio.py +22 -0
  10. machineconfig/jobs/script_installer/bypass_paywall.py +23 -0
  11. machineconfig/jobs/script_installer/code.py +34 -0
  12. machineconfig/jobs/script_installer/docker_desktop.py +41 -0
  13. machineconfig/jobs/script_installer/ngrok.py +29 -0
  14. machineconfig/jobs/{python_linux_installers → script_installer}/skim.py +21 -19
  15. machineconfig/jobs/script_installer/wezterm.py +34 -0
  16. machineconfig/profile/create.py +107 -200
  17. machineconfig/profile/shell.py +127 -0
  18. machineconfig/scripts/__init__.py +6 -6
  19. machineconfig/scripts/python/cloud_copy.py +93 -0
  20. machineconfig/scripts/python/cloud_manager.py +38 -0
  21. machineconfig/scripts/python/cloud_mount.py +115 -52
  22. machineconfig/scripts/python/cloud_repo_sync.py +154 -114
  23. machineconfig/scripts/python/cloud_sync.py +261 -79
  24. machineconfig/scripts/python/croshell.py +151 -0
  25. machineconfig/scripts/python/devops.py +119 -87
  26. machineconfig/scripts/python/devops_add_identity.py +27 -23
  27. machineconfig/scripts/python/devops_add_ssh_key.py +70 -55
  28. machineconfig/scripts/python/devops_backup_retrieve.py +52 -46
  29. machineconfig/scripts/python/devops_devapps_install.py +120 -91
  30. machineconfig/scripts/python/devops_update_repos.py +82 -68
  31. machineconfig/scripts/python/dotfile.py +42 -38
  32. machineconfig/scripts/python/fire_jobs.py +351 -98
  33. machineconfig/scripts/python/ftpx.py +82 -0
  34. machineconfig/scripts/python/mount_nfs.py +54 -3
  35. machineconfig/scripts/python/mount_nw_drive.py +31 -0
  36. machineconfig/scripts/python/mount_ssh.py +44 -20
  37. machineconfig/scripts/python/onetimeshare.py +60 -51
  38. machineconfig/scripts/python/pomodoro.py +41 -37
  39. machineconfig/scripts/python/repos.py +195 -128
  40. machineconfig/scripts/python/scheduler.py +52 -0
  41. machineconfig/scripts/python/snapshot.py +21 -21
  42. machineconfig/scripts/python/start_slidev.py +104 -0
  43. machineconfig/scripts/python/start_terminals.py +97 -0
  44. machineconfig/scripts/python/wifi_conn.py +90 -71
  45. machineconfig/scripts/python/{transfer_wsl_win.py → wsl_windows_transfer.py} +47 -39
  46. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +44 -48
  47. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +136 -130
  48. machineconfig/utils/installer.py +251 -0
  49. machineconfig/utils/procs.py +114 -64
  50. machineconfig/utils/scheduling.py +188 -0
  51. machineconfig/utils/utils.py +353 -249
  52. machineconfig/utils/ve.py +222 -0
  53. {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/METADATA +140 -110
  54. machineconfig-1.8.dist-info/RECORD +70 -0
  55. {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/WHEEL +1 -1
  56. machineconfig/jobs/python/python_linux_installers_all.py +0 -73
  57. machineconfig/jobs/python/python_ve_installer.py +0 -73
  58. machineconfig/jobs/python/python_windows_installers_all.py +0 -23
  59. machineconfig/jobs/python_generic_installers/archive/nvim.py +0 -15
  60. machineconfig/jobs/python_generic_installers/archive/strongbox.py +0 -32
  61. machineconfig/jobs/python_generic_installers/archive/vtm.py +0 -25
  62. machineconfig/jobs/python_generic_installers/broot.py +0 -39
  63. machineconfig/jobs/python_generic_installers/browsh.py +0 -25
  64. machineconfig/jobs/python_generic_installers/bw.py +0 -26
  65. machineconfig/jobs/python_generic_installers/chatgpt.py +0 -24
  66. machineconfig/jobs/python_generic_installers/cpufetch.py +0 -23
  67. machineconfig/jobs/python_generic_installers/delta.py +0 -59
  68. machineconfig/jobs/python_generic_installers/dev/__init__.py +0 -0
  69. machineconfig/jobs/python_generic_installers/dev/autogpt.py +0 -28
  70. machineconfig/jobs/python_generic_installers/dev/bw.py +0 -29
  71. machineconfig/jobs/python_generic_installers/dev/evcxr.py +0 -22
  72. machineconfig/jobs/python_generic_installers/dev/kondo.py +0 -21
  73. machineconfig/jobs/python_generic_installers/dev/lvim.py +0 -60
  74. machineconfig/jobs/python_generic_installers/dev/ngrok.py +0 -35
  75. machineconfig/jobs/python_generic_installers/dev/opencommit.py +0 -21
  76. machineconfig/jobs/python_generic_installers/dev/qrcp.py +0 -25
  77. machineconfig/jobs/python_generic_installers/dev/qrscan.py +0 -16
  78. machineconfig/jobs/python_generic_installers/dev/rust-analyzer.py +0 -24
  79. machineconfig/jobs/python_generic_installers/dev/termscp.py +0 -23
  80. machineconfig/jobs/python_generic_installers/dev/tldr.py +0 -25
  81. machineconfig/jobs/python_generic_installers/dev/tokei.py +0 -24
  82. machineconfig/jobs/python_generic_installers/diskonaut.py +0 -26
  83. machineconfig/jobs/python_generic_installers/dua.py +0 -21
  84. machineconfig/jobs/python_generic_installers/evcxr.py +0 -21
  85. machineconfig/jobs/python_generic_installers/gitui.py +0 -23
  86. machineconfig/jobs/python_generic_installers/gopass.py +0 -19
  87. machineconfig/jobs/python_generic_installers/helix.py +0 -45
  88. machineconfig/jobs/python_generic_installers/kondo.py +0 -20
  89. machineconfig/jobs/python_generic_installers/lf.py +0 -25
  90. machineconfig/jobs/python_generic_installers/lvim.py +0 -34
  91. machineconfig/jobs/python_generic_installers/mprocs.py +0 -20
  92. machineconfig/jobs/python_generic_installers/navi.py +0 -20
  93. machineconfig/jobs/python_generic_installers/ots.py +0 -26
  94. machineconfig/jobs/python_generic_installers/ouch.py +0 -25
  95. machineconfig/jobs/python_generic_installers/pomodoro.py +0 -19
  96. machineconfig/jobs/python_generic_installers/procs.py +0 -20
  97. machineconfig/jobs/python_generic_installers/qrcp.py +0 -22
  98. machineconfig/jobs/python_generic_installers/qrscan.py +0 -14
  99. machineconfig/jobs/python_generic_installers/rclone.py +0 -21
  100. machineconfig/jobs/python_generic_installers/rust-analyzer.py +0 -24
  101. machineconfig/jobs/python_generic_installers/tere.py +0 -26
  102. machineconfig/jobs/python_generic_installers/termscp.py +0 -23
  103. machineconfig/jobs/python_generic_installers/tldr.py +0 -24
  104. machineconfig/jobs/python_generic_installers/tokei.py +0 -21
  105. machineconfig/jobs/python_generic_installers/vtm.py +0 -26
  106. machineconfig/jobs/python_generic_installers/watchexec.py +0 -21
  107. machineconfig/jobs/python_linux_installers/archive/__init__.py +0 -0
  108. machineconfig/jobs/python_linux_installers/archive/ranger.py +0 -18
  109. machineconfig/jobs/python_linux_installers/bandwhich.py +0 -11
  110. machineconfig/jobs/python_linux_installers/bottom.py +0 -17
  111. machineconfig/jobs/python_linux_installers/btop.py +0 -17
  112. machineconfig/jobs/python_linux_installers/dev/bandwhich.py +0 -13
  113. machineconfig/jobs/python_linux_installers/dev/bytehound.py +0 -20
  114. machineconfig/jobs/python_linux_installers/dev/nnn.py +0 -21
  115. machineconfig/jobs/python_linux_installers/gotty.py +0 -16
  116. machineconfig/jobs/python_linux_installers/joshuto.py +0 -28
  117. machineconfig/jobs/python_linux_installers/mcfly.py +0 -12
  118. machineconfig/jobs/python_linux_installers/nnn.py +0 -18
  119. machineconfig/jobs/python_linux_installers/topgrade.py +0 -15
  120. machineconfig/jobs/python_linux_installers/viu.py +0 -19
  121. machineconfig/jobs/python_linux_installers/xplr.py +0 -22
  122. machineconfig/jobs/python_linux_installers/zellij.py +0 -31
  123. machineconfig/jobs/python_windows_installers/archive/ntop.py +0 -20
  124. machineconfig/jobs/python_windows_installers/bat.py +0 -16
  125. machineconfig/jobs/python_windows_installers/boxes.py +0 -19
  126. machineconfig/jobs/python_windows_installers/bypass_paywall.py +0 -18
  127. machineconfig/jobs/python_windows_installers/dev/bypass_paywall.py +0 -21
  128. machineconfig/jobs/python_windows_installers/dev/obs_background_removal_plugin.py +0 -20
  129. machineconfig/jobs/python_windows_installers/fd.py +0 -17
  130. machineconfig/jobs/python_windows_installers/fzf.py +0 -19
  131. machineconfig/jobs/python_windows_installers/obs_background_removal_plugin.py +0 -19
  132. machineconfig/jobs/python_windows_installers/rg.py +0 -15
  133. machineconfig/jobs/python_windows_installers/ugrep.py +0 -14
  134. machineconfig/jobs/python_windows_installers/zoomit.py +0 -20
  135. machineconfig/jobs/python_windows_installers/zoxide.py +0 -20
  136. machineconfig/profile/fix_shell_profiles.py +0 -8
  137. machineconfig/scripts/python/archive/__init__.py +0 -0
  138. machineconfig/scripts/python/archive/bu_gdrive_rx.py +0 -41
  139. machineconfig/scripts/python/archive/bu_gdrive_sx.py +0 -40
  140. machineconfig/scripts/python/archive/bu_onedrive_rx.py +0 -59
  141. machineconfig/scripts/python/archive/bu_onedrive_sx.py +0 -45
  142. machineconfig/scripts/python/chatgpt.py +0 -28
  143. machineconfig/scripts/python/choose_ohmybash_theme.py +0 -25
  144. machineconfig/scripts/python/choose_ohmyposh_theme.py +0 -40
  145. machineconfig/scripts/python/cloud_rx.py +0 -42
  146. machineconfig/scripts/python/cloud_sx.py +0 -40
  147. machineconfig/scripts/python/ftprx.py +0 -37
  148. machineconfig/scripts/python/ftpsx.py +0 -36
  149. machineconfig/scripts/python/im2text.py +0 -15
  150. machineconfig/scripts/python/tmate_conn.py +0 -28
  151. machineconfig/scripts/python/tmate_start.py +0 -31
  152. machineconfig/utils/to_exe.py +0 -7
  153. machineconfig-1.5.dist-info/RECORD +0 -147
  154. /machineconfig/jobs/{python_generic_installers/archive → script_installer}/__init__.py +0 -0
  155. {machineconfig-1.5.dist-info → machineconfig-1.8.dist-info}/top_level.txt +0 -0
@@ -1,98 +1,351 @@
1
-
2
- import crocodile.toolbox as tb
3
- from machineconfig.utils.utils import display_options, PROGRAM_PATH, get_current_ve, choose_ssh_host
4
- # https://github.com/pallets/click combine with fire. Consider
5
- # https://github.com/ceccopierangiolieugenio/pyTermTk for display_options build TUI
6
- # https://github.com/chriskiehl/Gooey build commandline interface
7
- import inspect
8
-
9
-
10
- def main():
11
- import argparse
12
- parser = argparse.ArgumentParser()
13
- parser.add_argument("-p", "--path", type=str, help="The directory containing the jobs", default=".")
14
- # optional flag for interactivity
15
- parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively")
16
- parser.add_argument("--debug", "-d", action="store_true", help="debug")
17
- parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
18
- parser.add_argument("--main", "-m", action="store_true", help="launch the main file")
19
- args = parser.parse_args()
20
- jobs_dir = args.path
21
- jobs_dir = tb.P(jobs_dir).expanduser().absolute()
22
- print(f"Seaching recursively for all python file in directory `{jobs_dir}`")
23
- py_files = jobs_dir.search("*.py", not_in=["__init__.py"], r=True).to_list()
24
- choice_file = display_options(msg="Choose a file to run", options=py_files, fzf=True, multi=False)
25
-
26
- if args.interactive is False: exe = "python"
27
- else: exe = "ipython -i"
28
- try:
29
- ve_name = get_current_ve()
30
- exe = f". activate_ve {ve_name}; {exe}"
31
- except NotImplementedError:
32
- print(f"Failed to detect virtual enviroment name.")
33
- pass
34
- if args.debug: command = f"{exe} -m pudb {choice_file} " # TODO: functions not supported yet in debug mode.
35
- elif args.main: command = f"{exe} {choice_file}"
36
- else:
37
- module, choice_function = choose_function(choice_file)
38
- if choice_function != "RUN AS MAIN":
39
- kgs1, kgs2 = interactively_run_function(module[choice_function])
40
- command = f"{exe} -m fire {choice_file} {choice_function} " + " ".join([f"--{k} {v}" for k, v in kgs1.items()])
41
- else: command = f"{exe} {choice_file} "
42
- if args.remote: return run_on_remote(choice_file, args=args)
43
- print("\n", command, "\n\n\n")
44
- PROGRAM_PATH.write_text(command)
45
-
46
-
47
- def choose_function(file_path):
48
- print(f"Loading {file_path} ...")
49
- module = tb.P(file_path).readit()
50
- module: tb.Struct
51
- module = module.filter(lambda k, v: "function" in str(type(v)))
52
- module.print()
53
- options = module.apply(lambda k, v: f"{k} -- {type(v)} {tb.Display.get_repr(v.__doc__, limit=150) if v.__doc__ is not None else 'No docs for this.'}").to_list()
54
- main_option = f"RUN AS MAIN -- {tb.Display.get_repr(module.__doc__, limit=150) if module.__doc__ is not None else 'No docs for this.'}"
55
- options.append(main_option)
56
- choice_function = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
57
- choice_function = choice_function.split(' -- ')[0]
58
- return module, choice_function
59
-
60
-
61
- def interactively_run_function(func):
62
- sig = inspect.signature(func)
63
- params = list(sig.parameters.values())
64
- args = []
65
- kwargs = {}
66
- for param in params:
67
- if param.annotation is not inspect.Parameter.empty: hint = f" ({param.annotation.__name__})"
68
- else: hint = ""
69
- if param.default is not inspect.Parameter.empty:
70
- default = param.default
71
- value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) (default = {default}) : ")
72
- if value == "": value = default
73
- else: value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) : ")
74
- try:
75
- if param.annotation is not inspect.Parameter.empty: value = param.annotation(value)
76
- except (TypeError, ValueError): raise ValueError(f"Invalid input: {value} is not of type {param.annotation}")
77
- if param.kind == inspect.Parameter.KEYWORD_ONLY: kwargs[param.name] = value
78
- else: args.append((param.name, value))
79
- args_to_kwargs = dict(args)
80
- return args_to_kwargs, kwargs
81
-
82
-
83
- def run_on_remote(func, args):
84
- host = choose_ssh_host(multi=False)
85
- from crocodile.cluster.remote_machine import RemoteMachine, RemoteMachineConfig
86
- config = RemoteMachineConfig(copy_repo=True, update_repo=False, update_essential_repos=True,
87
- notify_upon_completion=True, ssh_params=dict(host=host),
88
- # to_email=None, email_config_name='enaut',
89
- data=[],
90
- ipython=False, interactive=args.interactive, pdb=False, pudb=args.debug, wrap_in_try_except=False,
91
- transfer_method="sftp")
92
- m = RemoteMachine(func=func, func_kwargs=None, config=config)
93
- m.run()
94
-
95
-
96
- if __name__ == '__main__':
97
- main()
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
- import crocodile.toolbox as tb
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()