machineconfig 1.97__py3-none-any.whl → 2.1__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 (268) hide show
  1. machineconfig/cluster/cloud_manager.py +22 -29
  2. machineconfig/cluster/data_transfer.py +2 -3
  3. machineconfig/cluster/distribute.py +0 -2
  4. machineconfig/cluster/file_manager.py +4 -5
  5. machineconfig/cluster/job_params.py +1 -4
  6. machineconfig/cluster/loader_runner.py +8 -11
  7. machineconfig/cluster/remote_machine.py +4 -5
  8. machineconfig/cluster/script_execution.py +2 -2
  9. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  10. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +4 -6
  11. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  12. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +35 -75
  13. machineconfig/cluster/sessions_managers/wt_local.py +113 -185
  14. machineconfig/cluster/sessions_managers/wt_local_manager.py +127 -197
  15. machineconfig/cluster/sessions_managers/wt_remote.py +60 -67
  16. machineconfig/cluster/sessions_managers/wt_remote_manager.py +110 -149
  17. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +61 -64
  18. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +72 -172
  19. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +27 -60
  20. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +58 -137
  21. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +46 -74
  22. machineconfig/cluster/sessions_managers/zellij_local.py +91 -147
  23. machineconfig/cluster/sessions_managers/zellij_local_manager.py +165 -190
  24. machineconfig/cluster/sessions_managers/zellij_remote.py +51 -58
  25. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +40 -46
  26. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +19 -17
  27. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +30 -31
  28. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +64 -134
  29. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +7 -11
  30. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +27 -55
  31. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +14 -13
  32. machineconfig/cluster/templates/cli_click.py +0 -1
  33. machineconfig/cluster/templates/cli_gooey.py +0 -2
  34. machineconfig/cluster/templates/cli_trogon.py +0 -1
  35. machineconfig/cluster/templates/run_cloud.py +0 -1
  36. machineconfig/cluster/templates/run_cluster.py +0 -1
  37. machineconfig/cluster/templates/run_remote.py +0 -1
  38. machineconfig/cluster/templates/utils.py +27 -11
  39. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  40. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  41. machineconfig/jobs/python/check_installations.py +9 -9
  42. machineconfig/jobs/python/create_bootable_media.py +0 -2
  43. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  44. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  45. machineconfig/jobs/python/tasks.py +0 -1
  46. machineconfig/jobs/python/vscode/api.py +5 -5
  47. machineconfig/jobs/python/vscode/link_ve.py +20 -21
  48. machineconfig/jobs/python/vscode/select_interpreter.py +28 -29
  49. machineconfig/jobs/python/vscode/sync_code.py +14 -18
  50. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  51. machineconfig/jobs/python_custom_installers/archive/ngrok.py +15 -15
  52. machineconfig/jobs/python_custom_installers/dev/aider.py +10 -18
  53. machineconfig/jobs/python_custom_installers/dev/alacritty.py +12 -21
  54. machineconfig/jobs/python_custom_installers/dev/brave.py +13 -22
  55. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +13 -20
  56. machineconfig/jobs/python_custom_installers/dev/code.py +17 -24
  57. machineconfig/jobs/python_custom_installers/dev/cursor.py +10 -21
  58. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +12 -11
  59. machineconfig/jobs/python_custom_installers/dev/espanso.py +19 -23
  60. machineconfig/jobs/python_custom_installers/dev/goes.py +9 -16
  61. machineconfig/jobs/python_custom_installers/dev/lvim.py +13 -21
  62. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +15 -22
  63. machineconfig/jobs/python_custom_installers/dev/redis.py +15 -23
  64. machineconfig/jobs/python_custom_installers/dev/wezterm.py +15 -22
  65. machineconfig/jobs/python_custom_installers/dev/winget.py +32 -50
  66. machineconfig/jobs/python_custom_installers/docker.py +15 -24
  67. machineconfig/jobs/python_custom_installers/gh.py +18 -26
  68. machineconfig/jobs/python_custom_installers/hx.py +33 -17
  69. machineconfig/jobs/python_custom_installers/warp-cli.py +15 -23
  70. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  71. machineconfig/jobs/python_generic_installers/config.json +412 -389
  72. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  73. machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
  74. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  75. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  76. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  77. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  78. machineconfig/logger.py +50 -0
  79. machineconfig/profile/create.py +50 -36
  80. machineconfig/profile/create_hardlinks.py +33 -26
  81. machineconfig/profile/shell.py +87 -60
  82. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  83. machineconfig/scripts/cloud/init.sh +2 -2
  84. machineconfig/scripts/linux/checkout_versions +1 -1
  85. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  86. machineconfig/scripts/linux/cloud_copy +1 -1
  87. machineconfig/scripts/linux/cloud_manager +1 -1
  88. machineconfig/scripts/linux/cloud_mount +1 -1
  89. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  90. machineconfig/scripts/linux/cloud_sync +1 -1
  91. machineconfig/scripts/linux/croshell +1 -1
  92. machineconfig/scripts/linux/devops +3 -5
  93. machineconfig/scripts/linux/fire +2 -1
  94. machineconfig/scripts/linux/fire_agents +3 -3
  95. machineconfig/scripts/linux/ftpx +1 -1
  96. machineconfig/scripts/linux/gh_models +1 -1
  97. machineconfig/scripts/linux/kill_process +1 -1
  98. machineconfig/scripts/linux/mcinit +2 -2
  99. machineconfig/scripts/linux/repos +1 -1
  100. machineconfig/scripts/linux/scheduler +1 -1
  101. machineconfig/scripts/linux/start_slidev +1 -1
  102. machineconfig/scripts/linux/start_terminals +1 -1
  103. machineconfig/scripts/linux/url2md +1 -1
  104. machineconfig/scripts/linux/warp-cli.sh +122 -0
  105. machineconfig/scripts/linux/wifi_conn +1 -1
  106. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  107. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  111. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  112. machineconfig/scripts/python/ai/__init__.py +0 -0
  113. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  114. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  115. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  116. machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
  117. machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
  118. machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
  119. machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
  120. machineconfig/scripts/python/ai/generate_files.py +84 -0
  121. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
  122. machineconfig/scripts/python/ai/mcinit.py +107 -0
  123. machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
  124. machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
  125. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +52 -0
  126. machineconfig/scripts/python/archive/tmate_conn.py +5 -5
  127. machineconfig/scripts/python/archive/tmate_start.py +3 -3
  128. machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
  129. machineconfig/scripts/python/cloud_copy.py +20 -19
  130. machineconfig/scripts/python/cloud_mount.py +10 -8
  131. machineconfig/scripts/python/cloud_repo_sync.py +15 -15
  132. machineconfig/scripts/python/cloud_sync.py +1 -1
  133. machineconfig/scripts/python/croshell.py +18 -16
  134. machineconfig/scripts/python/devops.py +6 -6
  135. machineconfig/scripts/python/devops_add_identity.py +9 -7
  136. machineconfig/scripts/python/devops_add_ssh_key.py +19 -19
  137. machineconfig/scripts/python/devops_backup_retrieve.py +14 -14
  138. machineconfig/scripts/python/devops_devapps_install.py +3 -3
  139. machineconfig/scripts/python/devops_update_repos.py +141 -53
  140. machineconfig/scripts/python/dotfile.py +3 -3
  141. machineconfig/scripts/python/fire_agents.py +202 -41
  142. machineconfig/scripts/python/fire_jobs.py +20 -21
  143. machineconfig/scripts/python/ftpx.py +4 -3
  144. machineconfig/scripts/python/gh_models.py +94 -94
  145. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  146. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  147. machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
  148. machineconfig/scripts/python/helpers/helpers2.py +3 -3
  149. machineconfig/scripts/python/helpers/helpers4.py +8 -7
  150. machineconfig/scripts/python/helpers/helpers5.py +7 -7
  151. machineconfig/scripts/python/helpers/repo_sync_helpers.py +2 -2
  152. machineconfig/scripts/python/mount_nfs.py +4 -3
  153. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  154. machineconfig/scripts/python/mount_ssh.py +4 -3
  155. machineconfig/scripts/python/repos.py +9 -9
  156. machineconfig/scripts/python/scheduler.py +1 -1
  157. machineconfig/scripts/python/start_slidev.py +9 -8
  158. machineconfig/scripts/python/start_terminals.py +1 -1
  159. machineconfig/scripts/python/viewer.py +40 -40
  160. machineconfig/scripts/python/wifi_conn.py +65 -66
  161. machineconfig/scripts/python/wsl_windows_transfer.py +2 -2
  162. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  163. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  164. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  165. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  166. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  167. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  168. machineconfig/scripts/windows/croshell.ps1 +2 -2
  169. machineconfig/scripts/windows/devops.ps1 +1 -4
  170. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  171. machineconfig/scripts/windows/fire.ps1 +1 -1
  172. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  173. machineconfig/scripts/windows/gpt.ps1 +1 -1
  174. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  175. machineconfig/scripts/windows/mcinit.ps1 +2 -2
  176. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  177. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  178. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  179. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  180. machineconfig/scripts/windows/repos.ps1 +1 -1
  181. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  182. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  183. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  184. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  185. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  186. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  187. machineconfig/settings/lf/linux/lfrc +1 -1
  188. machineconfig/settings/linters/.ruff.toml +2 -2
  189. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  190. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  191. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  192. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
  193. machineconfig/settings/shells/wt/settings.json +8 -8
  194. machineconfig/settings/svim/linux/init.toml +1 -1
  195. machineconfig/settings/svim/windows/init.toml +1 -1
  196. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  197. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  198. machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
  199. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  200. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  201. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  202. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  203. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  204. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +75 -18
  205. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +52 -42
  206. machineconfig/utils/ai/browser_user_wrapper.py +5 -5
  207. machineconfig/utils/ai/generate_file_checklist.py +19 -22
  208. machineconfig/utils/ai/url2md.py +5 -3
  209. machineconfig/utils/cloud/onedrive/setup_oauth.py +5 -4
  210. machineconfig/utils/cloud/onedrive/transaction.py +192 -227
  211. machineconfig/utils/code.py +71 -43
  212. machineconfig/utils/installer.py +77 -85
  213. machineconfig/utils/installer_utils/installer_abc.py +29 -17
  214. machineconfig/utils/installer_utils/installer_class.py +188 -83
  215. machineconfig/utils/io_save.py +3 -15
  216. machineconfig/utils/links.py +22 -11
  217. machineconfig/utils/notifications.py +197 -0
  218. machineconfig/utils/options.py +38 -25
  219. machineconfig/utils/path.py +18 -6
  220. machineconfig/utils/path_reduced.py +637 -316
  221. machineconfig/utils/procs.py +69 -63
  222. machineconfig/utils/scheduling.py +11 -13
  223. machineconfig/utils/ssh.py +351 -0
  224. machineconfig/utils/terminal.py +225 -0
  225. machineconfig/utils/utils.py +13 -12
  226. machineconfig/utils/utils2.py +43 -10
  227. machineconfig/utils/utils5.py +242 -46
  228. machineconfig/utils/ve.py +11 -6
  229. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/METADATA +15 -9
  230. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/RECORD +232 -235
  231. machineconfig/cluster/self_ssh.py +0 -57
  232. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  233. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  234. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  235. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  236. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  237. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  238. machineconfig/jobs/python_generic_installers/update.py +0 -3
  239. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  240. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  241. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  242. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  243. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  244. machineconfig/scripts/linux/activate_ve +0 -87
  245. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  246. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  247. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  248. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  249. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  250. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  251. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  252. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  253. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  254. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  255. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  256. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  257. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  258. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  259. machineconfig/scripts/python/ai/init.py +0 -56
  260. machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
  261. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  262. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  263. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  264. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  265. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  266. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  267. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  268. {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -1,42 +1,75 @@
1
-
2
-
3
1
  from pathlib import Path
4
2
  from typing import Optional, Any
3
+ # import time
4
+ # from typing import Callable, Literal, TypeVar, ParamSpec
5
+
5
6
 
6
7
  def randstr(length: int = 10, lower: bool = True, upper: bool = True, digits: bool = True, punctuation: bool = False, safe: bool = False, noun: bool = False) -> str:
7
8
  if safe:
8
9
  import secrets
10
+
9
11
  return secrets.token_urlsafe(length) # interannly, it uses: random.SystemRandom or os.urandom which is hardware-based, not pseudo
10
12
  if noun:
11
13
  import randomname
14
+
12
15
  return randomname.get_name()
13
16
  import string
14
17
  import random
18
+
15
19
  population = (string.ascii_lowercase if lower else "") + (string.ascii_uppercase if upper else "") + (string.digits if digits else "") + (string.punctuation if punctuation else "")
16
- return ''.join(random.choices(population, k=length))
20
+ return "".join(random.choices(population, k=length))
17
21
 
18
22
 
19
- def read_ini(path: 'Path', encoding: Optional[str] = None):
20
- if not Path(path).exists() or Path(path).is_dir(): raise FileNotFoundError(f"File not found or is a directory: {path}")
23
+ def read_ini(path: "Path", encoding: Optional[str] = None):
24
+ if not Path(path).exists() or Path(path).is_dir():
25
+ raise FileNotFoundError(f"File not found or is a directory: {path}")
21
26
  import configparser
27
+
22
28
  res = configparser.ConfigParser()
23
29
  res.read(filenames=[str(path)], encoding=encoding)
24
30
  return res
25
- def read_json(path: 'Path', r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
31
+
32
+
33
+ def read_json(path: "Path", r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
26
34
  import json
35
+
27
36
  try:
28
- mydict = json.loads(Path(path).read_text(encoding='utf-8'), **kwargs)
37
+ mydict = json.loads(Path(path).read_text(encoding="utf-8"), **kwargs)
29
38
  except Exception:
30
39
  import pyjson5
31
- mydict = pyjson5.loads(Path(path).read_text(encoding='utf-8'), **kwargs) # file has C-style comments.
40
+
41
+ mydict = pyjson5.loads(Path(path).read_text(encoding="utf-8"), **kwargs) # file has C-style comments.
32
42
  _ = r
33
43
  return mydict
34
- def read_toml(path: 'Path'):
44
+
45
+
46
+ def read_toml(path: "Path"):
35
47
  import tomli
36
- return tomli.loads(path.read_text(encoding='utf-8'))
48
+
49
+ return tomli.loads(path.read_text(encoding="utf-8"))
50
+
37
51
 
38
52
  def pprint(obj: dict[Any, Any], title: str) -> None:
39
53
  from rich import inspect
40
54
  inspect(type("TempStruct", (object,), obj)(), value=False, title=title, docs=False, dunder=False, sort=False)
55
+
56
+
41
57
  def get_repr(obj: dict[Any, Any], sep: str = "\n", justify: int = 15, quotes: bool = False):
42
58
  return sep.join([f"{key:>{justify}} = {repr(val) if quotes else val}" for key, val in obj.items()])
59
+
60
+
61
+ def human_friendly_dict(d: dict[str, Any]) -> dict[str, Any]:
62
+ from datetime import datetime
63
+
64
+ result = {}
65
+ for k, v in d.items():
66
+ if isinstance(v, float):
67
+ result[k] = f"{v:.2f}"
68
+ elif isinstance(v, bool):
69
+ result[k] = "✓" if v else "✗"
70
+ elif isinstance(v, int) and len(str(v)) == 13 and v > 0: # assuming ms timestamp
71
+ dt = datetime.fromtimestamp(v / 1000)
72
+ result[k] = dt.strftime("%Y-%m-%d %H:%M")
73
+ else:
74
+ result[k] = v
75
+ return result
@@ -1,39 +1,68 @@
1
-
2
- from typing import Callable, Optional, Union, Any
3
- from logging import Logger as Log
4
- from machineconfig.utils.utils2 import randstr, get_repr
1
+ from pathlib import Path
2
+ from typing import Callable, Optional, Union, Any, NoReturn, TypeVar, Protocol, List, Generic
3
+ import logging
5
4
  import time
6
- from datetime import datetime, timezone
5
+ from datetime import datetime, timezone, timedelta
6
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
7
+
8
+
9
+ class LoggerTemplate(Protocol):
10
+ handlers: List[logging.Handler]
11
+
12
+ def debug(self, msg: str) -> None:
13
+ pass # 10
14
+
15
+ def info(self, msg: str) -> None:
16
+ pass # 20
17
+
18
+ def warning(self, msg: str) -> None:
19
+ pass # 30
20
+
21
+ def error(self, msg: str) -> None:
22
+ pass # 40
23
+
24
+ def critical(self, msg: str) -> None:
25
+ pass # 50
26
+
27
+ def fatal(self, msg: str) -> None:
28
+ pass # 50
7
29
 
8
30
 
9
31
  class Scheduler:
10
- def __init__(self, routine: Callable[['Scheduler'], Any],
11
- wait_ms: int,
12
- exception_handler: Optional[Callable[[Union[Exception, KeyboardInterrupt], str, 'Scheduler'], Any]] = None,
13
- logger: Optional[Log] = None,
14
- sess_stats: Optional[Callable[['Scheduler'], dict[str, Any]]] = None,
15
- max_cycles: int = 1_000_000_000,
16
- records: Optional[list[list[Any]]] = None):
32
+ def __init__(
33
+ self,
34
+ routine: Callable[["Scheduler"], Any],
35
+ wait_ms: int,
36
+ logger: LoggerTemplate,
37
+ sess_stats: Optional[Callable[["Scheduler"], dict[str, Any]]] = None,
38
+ exception_handler: Optional[Callable[[Union[Exception, KeyboardInterrupt], str, "Scheduler"], Any]] = None,
39
+ max_cycles: int = 1_000_000_000,
40
+ records: Optional[list[list[Any]]] = None,
41
+ ):
17
42
  self.routine = routine # main routine to be repeated every `wait` time period
18
- self.logger = logger if logger is not None else Log(name="SchedLogger_" + randstr(noun=True))
43
+ self.logger = logger
19
44
  self.exception_handler = exception_handler if exception_handler is not None else self.default_exception_handler
20
45
  self.records: list[list[Any]] = records if records is not None else []
21
46
  self.wait_ms = wait_ms # wait period between routine cycles.
22
47
  self.cycle: int = 0
23
48
  self.max_cycles: int = max_cycles
24
- self.sess_start_time_ms: int
49
+ self.sess_start_utc_ms: int
25
50
  self.sess_stats = sess_stats or (lambda _sched: {})
26
- def __repr__(self): return f"Scheduler with {self.cycle} cycles ran so far. Last cycle was at {self.sess_start_time_ms}."
27
- def run(self, max_cycles: Optional[int]=None, until_ms: Optional[int]=None):
51
+
52
+ def __repr__(self):
53
+ return f"Scheduler with {self.cycle} cycles ran so far. Last cycle was at {self.sess_start_utc_ms}."
54
+
55
+ def run(self, max_cycles: Optional[int] = None, until_ms: Optional[int] = None):
28
56
  if max_cycles is not None:
29
57
  self.max_cycles = max_cycles
30
58
  if until_ms is None:
31
- until_ms = 1_000_000_000_000
32
- self.sess_start_time_ms = time.time_ns() // 1_000_000
59
+ until_ms = 1_000_000_000_000_000
60
+ self.sess_start_utc_ms = time.time_ns() // 1_000_000
33
61
  while (time.time_ns() // 1_000_000) < until_ms and self.cycle < self.max_cycles:
34
62
  # 1- Time before Ops, and Opening Message
35
63
  time1_ms = time.time_ns() // 1_000_000
36
- self.logger.info(f"Starting Cycle {str(self.cycle).zfill(5)}. Total Run Time = {str(time1_ms - self.sess_start_time_ms).split('.', maxsplit=1)[0]}. UTC🕜 {datetime.now(tz=timezone.utc).strftime('%d %H:%M:%S')}")
64
+ duration = timedelta(milliseconds=time1_ms - self.sess_start_utc_ms)
65
+ self.logger.info(f"Starting Cycle {str(self.cycle).zfill(5)}. Total Run Time = {str(duration).split('.', maxsplit=1)[0]}. UTC🕜 {datetime.now(tz=timezone.utc).strftime('%d %H:%M:%S')}")
37
66
  try:
38
67
  self.routine(self)
39
68
  except Exception as ex:
@@ -41,44 +70,211 @@ class Scheduler:
41
70
  time2_ms = time.time_ns() // 1_000_000
42
71
  time_left_ms = int(self.wait_ms - (time2_ms - time1_ms)) # 4- Conclude Message
43
72
  self.cycle += 1
44
- self.logger.info(f"Finishing Cycle {str(self.cycle - 1).zfill(5)} in {str((time2_ms - time1_ms)*0.001).split('.', maxsplit=1)[0]}s. Sleeping for {self.wait_ms*0.001:0.1f}s ({time_left_ms*0.001:0.1}s left)\n" + "-" * 100)
45
- try: time.sleep(time_left_ms*0.001 if time_left_ms > 0 else 0.0) # # 5- Sleep. consider replacing by Asyncio.sleep
73
+ self.logger.info(f"Finishing Cycle {str(self.cycle - 1).zfill(5)} in {str((time2_ms - time1_ms) * 0.001).split('.', maxsplit=1)[0]}s. Sleeping for {self.wait_ms * 0.001:0.1f}s ({time_left_ms * 0.001:0.1f}s left)\n" + "-" * 100)
74
+ try:
75
+ time.sleep(time_left_ms * 0.001 if time_left_ms > 0 else 0.0) # # 5- Sleep. consider replacing by Asyncio.sleep
46
76
  except KeyboardInterrupt as ex:
47
77
  self.exception_handler(ex, "sleep", self)
48
78
  return # that's probably the only kind of exception that can rise during sleep.
49
79
  self.record_session_end(reason=f"Reached maximum number of cycles ({self.max_cycles})" if self.cycle >= self.max_cycles else f"Reached due stop time ({until_ms})")
50
- def get_records_df(self):
51
- import polars as pl
52
- columns = ["start", "finish", "duration", "cycles", "termination reason", "logfile"] + list(self.sess_stats(self).keys())
53
- return pl.DataFrame(self.records, schema=columns)
80
+
81
+ def get_records_df(self) -> List[dict[str, Any]]:
82
+ columns = ["start", "finish", "duration", "cycles", "termination reason"] + list(self.sess_stats(self).keys())
83
+ return [dict(zip(columns, row)) for row in self.records]
84
+
54
85
  def record_session_end(self, reason: str):
55
- import polars as pl
56
86
  end_time_ms = time.time_ns() // 1_000_000
57
- duration_ms = end_time_ms - self.sess_start_time_ms
87
+ duration_ms = end_time_ms - self.sess_start_utc_ms
58
88
  sess_stats = self.sess_stats(self)
59
- self.records.append([self.sess_start_time_ms, end_time_ms, duration_ms, self.cycle, reason,
60
- # self.logger.file_path
61
- ] + list(sess_stats.values()))
62
- summ = {"start time": f"{str(self.sess_start_time_ms)}",
63
- "finish time": f"{str(end_time_ms)}.",
64
- "duration": f"{str(duration_ms)} | wait time {self.wait_ms/1_000: 0.1f}s",
65
- "cycles ran": f"{self.cycle} | Lifetime cycles = {self.get_records_df().select(pl.col('cycles').sum()).item()}",
66
- "termination reason": reason,
67
- # "logfile": self.logger.file_path
68
- }
89
+ self.records.append(
90
+ [
91
+ self.sess_start_utc_ms,
92
+ end_time_ms,
93
+ duration_ms,
94
+ self.cycle,
95
+ reason,
96
+ # self.logger.file_path
97
+ ]
98
+ + list(sess_stats.values())
99
+ )
100
+ records_df = self.get_records_df()
101
+ total_cycles = sum(row["cycles"] for row in records_df)
102
+ summ = {
103
+ "start time": f"{str(self.sess_start_utc_ms)}",
104
+ "finish time": f"{str(end_time_ms)}.",
105
+ "duration": f"{str(duration_ms)} | wait time {self.wait_ms / 1_000: 0.1f}s",
106
+ "cycles ran": f"{self.cycle} | Lifetime cycles = {total_cycles}",
107
+ "termination reason": reason,
108
+ # "logfile": self.logger.file_path
109
+ }
69
110
  summ.update(sess_stats)
111
+ from machineconfig.utils.utils2 import get_repr
112
+
70
113
  tmp = get_repr(summ)
71
114
  self.logger.critical("\n--> Scheduler has finished running a session. \n" + tmp + "\n" + "-" * 100)
72
- df = self.get_records_df()
73
- df = df.with_columns([
74
- pl.col("start").map_elements(lambda x: str(x).split(".", maxsplit=1)[0], return_dtype=pl.String),
75
- pl.col("finish").map_elements(lambda x: str(x).split(".", maxsplit=1)[0], return_dtype=pl.String),
76
- pl.col("duration").map_elements(lambda x: str(x).split(".", maxsplit=1)[0], return_dtype=pl.String)
77
- ])
78
- self.logger.critical("\n--> Logger history.\n" + str(df))
115
+ # Format records as table
116
+ if records_df:
117
+ headers = list(records_df[0].keys())
118
+ # Process start, finish, duration to strings without milliseconds
119
+ processed_records = []
120
+ for row in records_df:
121
+ processed = row.copy()
122
+ processed["start"] = str(row["start"]).split(".", maxsplit=1)[0]
123
+ processed["finish"] = str(row["finish"]).split(".", maxsplit=1)[0]
124
+ processed["duration"] = str(row["duration"]).split(".", maxsplit=1)[0]
125
+ processed_records.append(processed)
126
+ # Simple aligned table formatting
127
+ max_lengths = {col: max(len(str(row.get(col, ""))) for row in processed_records) for col in headers}
128
+ table_lines = ["| " + " | ".join(col.ljust(max_lengths[col]) for col in headers) + " |"]
129
+ table_lines.append("|" + "-+-".join("-" * max_lengths[col] for col in headers) + "|")
130
+ for row in processed_records:
131
+ table_lines.append("| " + " | ".join(str(row.get(col, "")).ljust(max_lengths[col]) for col in headers) + " |")
132
+ table_str = "\n".join(table_lines)
133
+ else:
134
+ table_str = "No records available."
135
+ self.logger.critical("\n--> Logger history.\n" + table_str)
79
136
  return self
80
- def default_exception_handler(self, ex: Union[Exception, KeyboardInterrupt], during: str, sched: 'Scheduler') -> None: # user decides on handling and continue, terminate, save checkpoint, etc. # Use signal library.
137
+
138
+ def default_exception_handler(self, ex: Union[Exception, KeyboardInterrupt], during: str, sched: "Scheduler") -> None: # user decides on handling and continue, terminate, save checkpoint, etc. # Use signal library.
81
139
  print(sched)
82
140
  self.record_session_end(reason=f"during {during}, " + str(ex))
83
- self.logger.exception(ex)
141
+ self.logger.fatal(str(ex))
84
142
  raise ex
143
+
144
+
145
+ T = TypeVar("T")
146
+ T2 = TypeVar("T2")
147
+
148
+
149
+ class PrintFunc(Protocol):
150
+ def __call__(self, msg: str) -> Union[NoReturn, None]: ...
151
+
152
+ def to_pickle(obj: Any, path: Path) -> None:
153
+ import pickle
154
+ path.parent.mkdir(parents=True, exist_ok=True)
155
+ path.write_bytes(pickle.dumps(obj))
156
+ def from_pickle(path: Path) -> Any:
157
+ import pickle
158
+ return pickle.loads(path.read_bytes())
159
+
160
+
161
+ class Cache(Generic[T]): # This class helps to accelrate access to latest data coming from expensive function. The class has two flavours, memory-based and disk-based variants."""
162
+ # source_func: Callable[[], T]
163
+ def __init__(self, source_func: Callable[[], T],
164
+ expire: timedelta, logger: Optional[PrintFunc] = None, path: Optional[Path] = None,
165
+ saver: Callable[[T, Path], Any] = to_pickle, reader: Callable[[Path], T] = from_pickle, name: Optional[str] = None) -> None:
166
+ self.cache: T
167
+ self.source_func = source_func # function which when called returns a fresh object to be frozen.
168
+ self.path: Optional[PathExtended] = PathExtended(path) if path is not None else None # if path is passed, it will function as disk-based flavour.
169
+ self.time_produced = datetime.now() # if path is None else
170
+ self.save = saver
171
+ self.reader = reader
172
+ self.logger = logger
173
+ self.expire = expire
174
+ self.name = name if isinstance(name, str) else str(self.source_func)
175
+ self.last_call_is_fresh = False
176
+ @property
177
+ def age(self):
178
+ """Throws AttributeError if called before cache is populated and path doesn't exists"""
179
+ if self.path is None: # memory-based cache.
180
+ return datetime.now() - self.time_produced
181
+ return datetime.now() - datetime.fromtimestamp(self.path.stat().st_mtime)
182
+ # def __setstate__(self, state: dict[str, Any]) -> None:
183
+ # self.__dict__.update(state)
184
+ # self.path = P.home() / self.path if self.path is not None else self.path
185
+ # def __getstate__(self) -> dict[str, Any]:
186
+ # state = self.__dict__.copy()
187
+ # state["path"] = self.path.rel2home() if self.path is not None else state["path"]
188
+ # return state # With this implementation, instances can be pickled and loaded up in different machine and still works.
189
+ def __call__(self, fresh: bool = False) -> T:
190
+ self.last_call_is_fresh = False
191
+ if fresh or not hasattr(self, "cache"): # populate cache for the first time
192
+ if not fresh and self.path is not None and self.path.exists():
193
+ age = datetime.now() - datetime.fromtimestamp(self.path.stat().st_mtime)
194
+ msg1 = f"""
195
+ 📦 ════════════════════ CACHE OPERATION ════════════════════
196
+ 🔄 {self.name} cache: Reading cached values from `{self.path}`
197
+ ⏱️ Lag = {age}
198
+ ════════════════════════════════════════════════════════════
199
+ """
200
+ try:
201
+ self.cache = self.reader(self.path)
202
+ except Exception as ex:
203
+ if self.logger:
204
+ msg2 = f"""
205
+ ❌ ════════════════════ CACHE ERROR ════════════════════
206
+ ⚠️ {self.name} cache: Cache file is corrupted
207
+ 🔍 Error: {ex}
208
+ ════════════════════════════════════════════════════════
209
+ """
210
+ self.logger(msg1 + msg2)
211
+ self.cache = self.source_func()
212
+ self.last_call_is_fresh = True
213
+ self.time_produced = datetime.now()
214
+ # if self.path is not None:
215
+ # self.save(self.cache, self.path)
216
+ return self.cache
217
+ return self(fresh=False) # may be the cache is old ==> check that by passing it through the logic again.
218
+ else:
219
+ if self.logger:
220
+ # Previous cache never existed or there was an explicit fresh order.
221
+ why = "There was an explicit fresh order." if fresh else "Previous cache never existed or is corrupted."
222
+ self.logger(f"""
223
+ 🆕 ════════════════════ NEW CACHE ════════════════════
224
+ 🔄 {self.name} cache: Populating fresh cache from source func
225
+ ℹ️ Reason: {why}
226
+ ════════════════════════════════════════════════════════
227
+ """)
228
+ self.cache = self.source_func() # fresh data.
229
+ self.last_call_is_fresh = True
230
+ self.time_produced = datetime.now()
231
+ if self.path is not None: self.save(self.cache, self.path)
232
+ else: # cache exists
233
+ try: age = self.age
234
+ except AttributeError: # path doesn't exist (may be deleted) ==> need to repopulate cache form source_func.
235
+ return self(fresh=True)
236
+ if age > self.expire:
237
+ if self.logger:
238
+ self.logger(f"""
239
+ 🔄 ════════════════════ CACHE UPDATE ════════════════════
240
+ ⚠️ {self.name} cache: Updating cache from source func
241
+ ⏱️ Age = {age} > {self.expire}
242
+ ════════════════════════════════════════════════════════""")
243
+ self.cache = self.source_func()
244
+ self.last_call_is_fresh = True
245
+ self.time_produced = datetime.now()
246
+ if self.path is not None: self.save(self.cache, self.path)
247
+ else:
248
+ if self.logger:
249
+ self.logger(f"""
250
+ ✅ ════════════════════ USING CACHE ════════════════════
251
+ 📦 {self.name} cache: Using cached values
252
+ ⏱️ Lag = {age}
253
+ ════════════════════════════════════════════════════════""")
254
+ return self.cache
255
+ @staticmethod
256
+ def as_decorator(expire: timedelta, logger: Optional[PrintFunc] = None, path: Optional[Path] = None,
257
+ saver: Callable[[T2, Path], Any] = to_pickle,
258
+ reader: Callable[[Path], T2] = from_pickle,
259
+ name: Optional[str] = None): # -> Callable[..., 'Cache[T2]']:
260
+ def decorator(source_func: Callable[[], T2]) -> Cache['T2']:
261
+ res = Cache(source_func=source_func, expire=expire, logger=logger, path=path, name=name, reader=reader, saver=saver)
262
+ return res
263
+ return decorator
264
+ def from_cloud(self, cloud: str, rel2home: bool = True, root: Optional[str] = None):
265
+ assert self.path is not None
266
+ exists = self.path.exists()
267
+ exists_but_old = exists and ((datetime.now() - datetime.fromtimestamp(self.path.stat().st_mtime)) > self.expire)
268
+ if not exists or exists_but_old:
269
+ returned_path = self.path.from_cloud(cloud=cloud, rel2home=rel2home, root=root)
270
+ if returned_path is None and not exists:
271
+ raise FileNotFoundError(f"❌ Failed to get @ {self.path}. Build the cache first with signed API.")
272
+ elif returned_path is None and exists and self.logger is not None:
273
+ self.logger(f"""
274
+ ⚠️ ════════════════════ CLOUD FETCH WARNING ════════════════════
275
+ 🔄 Failed to get fresh data from cloud
276
+ 📦 Using old cache @ {self.path}
277
+ ════════════════════════════════════════════════════════════════""")
278
+ else:
279
+ pass # maybe we don't need to fetch it from cloud, if its too hot
280
+ return self.reader(self.path)
machineconfig/utils/ve.py CHANGED
@@ -1,4 +1,4 @@
1
- from machineconfig.utils.path_reduced import P as PathExtended
1
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
2
2
  from machineconfig.utils.utils2 import read_ini
3
3
  import platform
4
4
  from typing import Optional
@@ -19,10 +19,10 @@ def get_ve_path_and_ipython_profile(init_path: PathExtended) -> tuple[Optional[s
19
19
  ipy_profile = ini["specs"]["ipy_profile"]
20
20
  print(f"✨ Using IPython profile: {ipy_profile}")
21
21
  if ipy_profile is None and tmp.joinpath(".ipy_profile").exists():
22
- ipy_profile = tmp.joinpath(".ipy_profile").read_text().rstrip()
22
+ ipy_profile = tmp.joinpath(".ipy_profile").read_text(encoding="utf-8").rstrip()
23
23
  print(f"✨ Using IPython profile: {ipy_profile}. This is based on this file {tmp.joinpath('.ipy_profile')}")
24
24
  if ve_path is None and tmp.joinpath(".ve_path").exists():
25
- ve_path = tmp.joinpath(".ve_path").read_text().rstrip().replace("\n", "")
25
+ ve_path = tmp.joinpath(".ve_path").read_text(encoding="utf-8").rstrip().replace("\n", "")
26
26
  print(f"🔮 Using Virtual Environment found @ {tmp}/.ve_path: {ve_path}")
27
27
  if ve_path is None and tmp.joinpath(".venv").exists():
28
28
  print(f"🔮 Using Virtual Environment found @ {tmp}/.venv")
@@ -37,6 +37,7 @@ def get_ve_path_and_ipython_profile(init_path: PathExtended) -> tuple[Optional[s
37
37
 
38
38
  def get_repo_root(choice_file: str) -> Optional[str]:
39
39
  from git import Repo, InvalidGitRepositoryError
40
+
40
41
  try:
41
42
  repo = Repo(PathExtended(choice_file), search_parent_directories=True)
42
43
  repo_root = str(repo.working_tree_dir) if repo.working_tree_dir else None
@@ -44,8 +45,12 @@ def get_repo_root(choice_file: str) -> Optional[str]:
44
45
  repo_root = None
45
46
  return repo_root
46
47
 
48
+
47
49
  def get_ve_activate_line(ve_root: str):
48
- if platform.system() == "Windows": activate_ve_line = f". {ve_root}/Scripts/activate.ps1"
49
- elif platform.system() in ["Linux", "Darwin"]: activate_ve_line = f". {ve_root}/bin/activate"
50
- else: raise NotImplementedError(f"Platform {platform.system()} not supported.")
50
+ if platform.system() == "Windows":
51
+ activate_ve_line = f". {ve_root}/Scripts/activate.ps1"
52
+ elif platform.system() in ["Linux", "Darwin"]:
53
+ activate_ve_line = f". {ve_root}/bin/activate"
54
+ else:
55
+ raise NotImplementedError(f"Platform {platform.system()} not supported.")
51
56
  return activate_ve_line
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: machineconfig
3
- Version: 1.97
3
+ Version: 2.1
4
4
  Summary: Dotfiles management package
5
5
  Author-email: Alex Al-Saffar <programmer@usa.com>
6
6
  License: Apache 2.0
@@ -9,22 +9,28 @@ Project-URL: Bug Tracker, https://github.com/thisismygitrepo/machineconfig/issue
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.11
12
+ Requires-Python: >=3.13
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: crocodile
15
14
  Requires-Dist: rich>=14.0.0
16
15
  Requires-Dist: paramiko>=3.5.1
17
16
  Requires-Dist: psutil>=7.0.0
18
- Requires-Dist: openai>=1.75.0
19
- Requires-Dist: nbformat>=5.10.4
20
17
  Requires-Dist: fire>=0.7.0
21
18
  Requires-Dist: pydantic>=2.11.3
22
- Requires-Dist: clipboard>=0.0.4
23
19
  Requires-Dist: gitpython>=3.1.44
24
- Requires-Dist: pudb>=2024.1.3
25
20
  Requires-Dist: pyfzf>=0.3.1
26
- Requires-Dist: call-function-with-timeout>=1.1.1
27
21
  Requires-Dist: rclone-python>=0.1.23
22
+ Requires-Dist: pytz>=2025.2
23
+ Requires-Dist: tomli>=2.2.1
24
+ Requires-Dist: toml>=0.10.2
25
+ Requires-Dist: pyyaml>=6.0.2
26
+ Requires-Dist: pyjson5>=1.6.9
27
+ Requires-Dist: requests>=2.32.5
28
+ Requires-Dist: tqdm>=4.67.1
29
+ Requires-Dist: joblib>=1.5.2
30
+ Requires-Dist: randomname>=0.2.1
31
+ Requires-Dist: cryptography>=44.0.2
32
+ Requires-Dist: tenacity>=9.1.2
33
+ Requires-Dist: markdown>=3.9
28
34
  Provides-Extra: windows
29
35
  Requires-Dist: pywin32; extra == "windows"
30
36
  Provides-Extra: docs
@@ -93,7 +99,7 @@ iwr https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/mac
93
99
  (iwr bit.ly/cfgwt).Content | iex
94
100
  . $HOME/code/machineconfig/src/machineconfig/setup_windows/symlinks.ps1
95
101
 
96
- & "$HOME\venvs\ve\Scripts\activate.ps1"
102
+ & "$HOME\code\machineconfig\.venv\Scripts\activate.ps1"
97
103
  python -m fire machineconfig.profile.create main2 --choice=all
98
104
  deactivate
99
105