machineconfig 2.0__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 (235) hide show
  1. machineconfig/cluster/cloud_manager.py +0 -3
  2. machineconfig/cluster/data_transfer.py +0 -1
  3. machineconfig/cluster/file_manager.py +0 -1
  4. machineconfig/cluster/job_params.py +0 -3
  5. machineconfig/cluster/loader_runner.py +0 -3
  6. machineconfig/cluster/remote_machine.py +0 -1
  7. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  8. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +3 -5
  9. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
  11. machineconfig/cluster/sessions_managers/wt_local.py +36 -110
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
  13. machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
  20. machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
  30. machineconfig/cluster/templates/cli_click.py +0 -1
  31. machineconfig/cluster/templates/cli_gooey.py +0 -2
  32. machineconfig/cluster/templates/cli_trogon.py +0 -1
  33. machineconfig/cluster/templates/run_cloud.py +0 -1
  34. machineconfig/cluster/templates/run_cluster.py +0 -1
  35. machineconfig/cluster/templates/run_remote.py +0 -1
  36. machineconfig/cluster/templates/utils.py +26 -10
  37. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  38. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  39. machineconfig/jobs/python/check_installations.py +1 -0
  40. machineconfig/jobs/python/create_bootable_media.py +0 -2
  41. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  42. machineconfig/jobs/python/tasks.py +0 -1
  43. machineconfig/jobs/python/vscode/api.py +5 -5
  44. machineconfig/jobs/python/vscode/link_ve.py +13 -14
  45. machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
  46. machineconfig/jobs/python/vscode/sync_code.py +9 -13
  47. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  48. machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
  49. machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
  50. machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
  51. machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
  52. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
  53. machineconfig/jobs/python_custom_installers/dev/code.py +14 -21
  54. machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
  55. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
  56. machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
  57. machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
  58. machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
  59. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
  60. machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
  61. machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
  62. machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
  63. machineconfig/jobs/python_custom_installers/docker.py +12 -21
  64. machineconfig/jobs/python_custom_installers/gh.py +11 -19
  65. machineconfig/jobs/python_custom_installers/hx.py +32 -16
  66. machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
  67. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  68. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  70. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  71. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  72. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  73. machineconfig/profile/create.py +29 -22
  74. machineconfig/profile/create_hardlinks.py +26 -19
  75. machineconfig/profile/shell.py +51 -28
  76. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  77. machineconfig/scripts/cloud/init.sh +2 -2
  78. machineconfig/scripts/linux/checkout_versions +1 -1
  79. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  80. machineconfig/scripts/linux/cloud_copy +1 -1
  81. machineconfig/scripts/linux/cloud_manager +1 -1
  82. machineconfig/scripts/linux/cloud_mount +1 -1
  83. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  84. machineconfig/scripts/linux/cloud_sync +1 -1
  85. machineconfig/scripts/linux/croshell +1 -1
  86. machineconfig/scripts/linux/devops +4 -6
  87. machineconfig/scripts/linux/fire +1 -1
  88. machineconfig/scripts/linux/fire_agents +3 -2
  89. machineconfig/scripts/linux/ftpx +1 -1
  90. machineconfig/scripts/linux/gh_models +1 -1
  91. machineconfig/scripts/linux/kill_process +1 -1
  92. machineconfig/scripts/linux/mcinit +1 -1
  93. machineconfig/scripts/linux/repos +1 -1
  94. machineconfig/scripts/linux/scheduler +1 -1
  95. machineconfig/scripts/linux/start_slidev +1 -1
  96. machineconfig/scripts/linux/start_terminals +1 -1
  97. machineconfig/scripts/linux/url2md +1 -1
  98. machineconfig/scripts/linux/warp-cli.sh +122 -0
  99. machineconfig/scripts/linux/wifi_conn +1 -1
  100. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  101. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  106. machineconfig/scripts/python/ai/__init__.py +0 -0
  107. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/ai/generate_files.py +84 -0
  111. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
  112. machineconfig/scripts/python/ai/mcinit.py +7 -3
  113. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
  114. machineconfig/scripts/python/cloud_copy.py +1 -1
  115. machineconfig/scripts/python/cloud_mount.py +1 -1
  116. machineconfig/scripts/python/cloud_repo_sync.py +4 -4
  117. machineconfig/scripts/python/croshell.py +5 -3
  118. machineconfig/scripts/python/devops_add_identity.py +1 -1
  119. machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
  120. machineconfig/scripts/python/devops_backup_retrieve.py +1 -1
  121. machineconfig/scripts/python/devops_update_repos.py +140 -52
  122. machineconfig/scripts/python/dotfile.py +1 -1
  123. machineconfig/scripts/python/fire_agents.py +28 -9
  124. machineconfig/scripts/python/fire_jobs.py +3 -4
  125. machineconfig/scripts/python/ftpx.py +2 -1
  126. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  127. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  128. machineconfig/scripts/python/helpers/helpers2.py +2 -2
  129. machineconfig/scripts/python/helpers/helpers4.py +1 -2
  130. machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
  131. machineconfig/scripts/python/mount_nfs.py +1 -1
  132. machineconfig/scripts/python/mount_ssh.py +1 -1
  133. machineconfig/scripts/python/repos.py +1 -1
  134. machineconfig/scripts/python/start_slidev.py +1 -1
  135. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  136. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  137. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  138. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  139. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  140. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  141. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  142. machineconfig/scripts/windows/croshell.ps1 +2 -2
  143. machineconfig/scripts/windows/devops.ps1 +1 -4
  144. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  145. machineconfig/scripts/windows/fire.ps1 +1 -1
  146. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  147. machineconfig/scripts/windows/gpt.ps1 +1 -1
  148. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  149. machineconfig/scripts/windows/mcinit.ps1 +1 -1
  150. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  151. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  152. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  153. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  154. machineconfig/scripts/windows/repos.ps1 +1 -1
  155. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  156. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  157. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  158. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  159. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  160. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  161. machineconfig/settings/lf/linux/lfrc +1 -1
  162. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  163. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  164. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  165. machineconfig/settings/svim/linux/init.toml +1 -1
  166. machineconfig/settings/svim/windows/init.toml +1 -1
  167. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  168. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  169. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  170. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  171. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  172. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  173. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  174. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +66 -12
  175. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +44 -36
  176. machineconfig/utils/ai/generate_file_checklist.py +8 -10
  177. machineconfig/utils/ai/url2md.py +4 -2
  178. machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
  179. machineconfig/utils/cloud/onedrive/transaction.py +63 -98
  180. machineconfig/utils/code.py +60 -39
  181. machineconfig/utils/installer.py +27 -33
  182. machineconfig/utils/installer_utils/installer_abc.py +8 -7
  183. machineconfig/utils/installer_utils/installer_class.py +149 -70
  184. machineconfig/utils/links.py +22 -11
  185. machineconfig/utils/notifications.py +197 -0
  186. machineconfig/utils/options.py +29 -23
  187. machineconfig/utils/path.py +13 -6
  188. machineconfig/utils/path_reduced.py +485 -216
  189. machineconfig/utils/procs.py +47 -41
  190. machineconfig/utils/scheduling.py +0 -1
  191. machineconfig/utils/ssh.py +157 -76
  192. machineconfig/utils/terminal.py +82 -37
  193. machineconfig/utils/utils.py +12 -10
  194. machineconfig/utils/utils2.py +38 -48
  195. machineconfig/utils/utils5.py +183 -116
  196. machineconfig/utils/ve.py +9 -4
  197. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/METADATA +3 -2
  198. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/RECORD +200 -217
  199. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  200. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  201. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  202. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  203. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  204. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  205. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  206. machineconfig/jobs/python_generic_installers/update.py +0 -3
  207. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  208. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  209. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  210. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  211. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  212. machineconfig/scripts/linux/activate_ve +0 -87
  213. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  214. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  215. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  216. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  217. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  218. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  219. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  220. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  221. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  222. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  223. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  224. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  225. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  226. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  227. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  228. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  229. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  230. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  231. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  232. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  233. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  234. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  235. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -53,36 +53,24 @@ class WTLocalManager:
53
53
  script_path = manager.script_path
54
54
 
55
55
  if not script_path:
56
- results[session_name] = {
57
- "success": False,
58
- "error": "No script file path available"
59
- }
56
+ results[session_name] = {"success": False, "error": "No script file path available"}
60
57
  continue
61
58
 
62
59
  # Execute the PowerShell script to start Windows Terminal
63
- cmd = f"powershell -ExecutionPolicy Bypass -File \"{script_path}\""
60
+ cmd = f'powershell -ExecutionPolicy Bypass -File "{script_path}"'
64
61
 
65
62
  logger.info(f"Starting session '{session_name}' with script: {script_path}")
66
63
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30)
67
64
 
68
65
  if result.returncode == 0:
69
- results[session_name] = {
70
- "success": True,
71
- "message": f"Session '{session_name}' started successfully"
72
- }
66
+ results[session_name] = {"success": True, "message": f"Session '{session_name}' started successfully"}
73
67
  logger.info(f"✅ Session '{session_name}' started successfully")
74
68
  else:
75
- results[session_name] = {
76
- "success": False,
77
- "error": result.stderr or result.stdout
78
- }
69
+ results[session_name] = {"success": False, "error": result.stderr or result.stdout}
79
70
  logger.error(f"❌ Failed to start session '{session_name}': {result.stderr}")
80
71
 
81
72
  except Exception as e:
82
- results[session_name] = {
83
- "success": False,
84
- "error": str(e)
85
- }
73
+ results[session_name] = {"success": False, "error": str(e)}
86
74
  logger.error(f"❌ Exception starting session '{session_name}': {e}")
87
75
 
88
76
  return results
@@ -99,16 +87,10 @@ class WTLocalManager:
99
87
  logger.info(f"Killing Windows Terminal processes for session '{session_name}'")
100
88
  result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
101
89
 
102
- results[session_name] = {
103
- "success": result.returncode == 0,
104
- "message": "Windows Terminal processes killed" if result.returncode == 0 else result.stderr
105
- }
90
+ results[session_name] = {"success": result.returncode == 0, "message": "Windows Terminal processes killed" if result.returncode == 0 else result.stderr}
106
91
 
107
92
  except Exception as e:
108
- results[session_name] = {
109
- "success": False,
110
- "error": str(e)
111
- }
93
+ results[session_name] = {"success": False, "error": str(e)}
112
94
 
113
95
  return results
114
96
 
@@ -157,12 +139,7 @@ class WTLocalManager:
157
139
  status_report[session_name] = {
158
140
  "session_status": session_status,
159
141
  "commands_status": commands_status,
160
- "summary": {
161
- "total_commands": total_count,
162
- "running_commands": running_count,
163
- "stopped_commands": total_count - running_count,
164
- "session_healthy": session_status.get("session_exists", False)
165
- }
142
+ "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": session_status.get("session_exists", False)},
166
143
  }
167
144
 
168
145
  return status_report
@@ -172,12 +149,9 @@ class WTLocalManager:
172
149
  all_status = self.check_all_sessions_status()
173
150
 
174
151
  total_sessions = len(all_status)
175
- healthy_sessions = sum(1 for status in all_status.values()
176
- if status["summary"]["session_healthy"])
177
- total_commands = sum(status["summary"]["total_commands"]
178
- for status in all_status.values())
179
- total_running = sum(status["summary"]["running_commands"]
180
- for status in all_status.values())
152
+ healthy_sessions = sum(1 for status in all_status.values() if status["summary"]["session_healthy"])
153
+ total_commands = sum(status["summary"]["total_commands"] for status in all_status.values())
154
+ total_running = sum(status["summary"]["running_commands"] for status in all_status.values())
181
155
 
182
156
  return {
183
157
  "total_sessions": total_sessions,
@@ -187,7 +161,7 @@ class WTLocalManager:
187
161
  "running_commands": total_running,
188
162
  "stopped_commands": total_commands - total_running,
189
163
  "all_sessions_healthy": healthy_sessions == total_sessions,
190
- "all_commands_running": total_running == total_commands
164
+ "all_commands_running": total_running == total_commands,
191
165
  }
192
166
 
193
167
  def print_status_report(self) -> None:
@@ -253,6 +227,7 @@ class WTLocalManager:
253
227
  Args:
254
228
  wait_ms: How long to wait between checks in milliseconds (default: 30000ms = 30s)
255
229
  """
230
+
256
231
  def routine(scheduler: Scheduler):
257
232
  print(f"\n⏰ Monitoring cycle {scheduler.cycle} at {datetime.now()}")
258
233
  print("-" * 50)
@@ -265,13 +240,15 @@ class WTLocalManager:
265
240
  status_data = []
266
241
  for session_name, status in all_status.items():
267
242
  for tab_name, cmd_status in status["commands_status"].items():
268
- status_data.append({
269
- "session": session_name,
270
- "tab": tab_name,
271
- "running": cmd_status.get("running", False),
272
- "command": cmd_status.get("command", "Unknown")[:50] + "..." if len(cmd_status.get("command", "")) > 50 else cmd_status.get("command", ""),
273
- "processes": len(cmd_status.get("processes", []))
274
- })
243
+ status_data.append(
244
+ {
245
+ "session": session_name,
246
+ "tab": tab_name,
247
+ "running": cmd_status.get("running", False),
248
+ "command": cmd_status.get("command", "Unknown")[:50] + "..." if len(cmd_status.get("command", "")) > 50 else cmd_status.get("command", ""),
249
+ "processes": len(cmd_status.get("processes", [])),
250
+ }
251
+ )
275
252
 
276
253
  if status_data:
277
254
  # Format data as table
@@ -316,13 +293,7 @@ class WTLocalManager:
316
293
  config_file.write_text(text, encoding="utf-8")
317
294
 
318
295
  # Save metadata
319
- metadata = {
320
- "session_name_prefix": self.session_name_prefix,
321
- "created_at": str(datetime.now()),
322
- "num_managers": len(self.managers),
323
- "sessions": list(self.session2wt_tabs.keys()),
324
- "manager_type": "WTLocalManager"
325
- }
296
+ metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "sessions": list(self.session2wt_tabs.keys()), "manager_type": "WTLocalManager"}
326
297
  metadata_file = session_dir / "metadata.json"
327
298
  text = json.dumps(metadata, indent=2, ensure_ascii=False)
328
299
  metadata_file.write_text(text, encoding="utf-8")
@@ -332,11 +303,7 @@ class WTLocalManager:
332
303
  managers_dir.mkdir(exist_ok=True)
333
304
 
334
305
  for i, manager in enumerate(self.managers):
335
- manager_data = {
336
- "session_name": manager.session_name,
337
- "tab_config": manager.tab_config,
338
- "script_path": manager.script_path
339
- }
306
+ manager_data = {"session_name": manager.session_name, "tab_config": manager.tab_config, "script_path": manager.script_path}
340
307
  manager_file = managers_dir / f"manager_{i}_{manager.session_name}.json"
341
308
  text = json.dumps(manager_data, indent=2, ensure_ascii=False)
342
309
  manager_file.write_text(text, encoding="utf-8")
@@ -345,7 +312,7 @@ class WTLocalManager:
345
312
  return session_id
346
313
 
347
314
  @classmethod
348
- def load(cls, session_id: str) -> 'WTLocalManager':
315
+ def load(cls, session_id: str) -> "WTLocalManager":
349
316
  """Load a saved manager state from disk."""
350
317
  session_dir = TMP_SERIALIZATION_DIR / session_id
351
318
 
@@ -357,14 +324,14 @@ class WTLocalManager:
357
324
  if not config_file.exists():
358
325
  raise FileNotFoundError(f"Configuration file not found: {config_file}")
359
326
 
360
- with open(config_file, 'r', encoding='utf-8') as f:
327
+ with open(config_file, "r", encoding="utf-8") as f:
361
328
  session2wt_tabs = json.load(f)
362
329
 
363
330
  # Load metadata
364
331
  metadata_file = session_dir / "metadata.json"
365
332
  session_name_prefix = "LocalWTMgr" # default fallback
366
333
  if metadata_file.exists():
367
- with open(metadata_file, 'r', encoding='utf-8') as f:
334
+ with open(metadata_file, "r", encoding="utf-8") as f:
368
335
  metadata = json.load(f)
369
336
  session_name_prefix = metadata.get("session_name_prefix", "LocalWTMgr")
370
337
 
@@ -379,7 +346,7 @@ class WTLocalManager:
379
346
 
380
347
  for manager_file in manager_files:
381
348
  try:
382
- with open(manager_file, 'r', encoding='utf-8') as f:
349
+ with open(manager_file, "r", encoding="utf-8") as f:
383
350
  manager_data = json.load(f)
384
351
 
385
352
  # Recreate the manager
@@ -420,6 +387,7 @@ class WTLocalManager:
420
387
 
421
388
  try:
422
389
  import shutil
390
+
423
391
  shutil.rmtree(session_dir)
424
392
  logger.info(f"✅ Deleted session: {session_id}")
425
393
  return True
@@ -434,15 +402,12 @@ class WTLocalManager:
434
402
  try:
435
403
  # Get all running Windows Terminal processes
436
404
  result = subprocess.run(
437
- ['powershell', '-Command',
438
- 'Get-Process -Name "WindowsTerminal" -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, MainWindowTitle | ConvertTo-Json -Depth 2'],
439
- capture_output=True,
440
- text=True,
441
- timeout=10
405
+ ["powershell", "-Command", 'Get-Process -Name "WindowsTerminal" -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, MainWindowTitle | ConvertTo-Json -Depth 2'], capture_output=True, text=True, timeout=10
442
406
  )
443
407
 
444
408
  if result.returncode == 0 and result.stdout.strip():
445
409
  import json
410
+
446
411
  all_processes = json.loads(result.stdout.strip())
447
412
  if not isinstance(all_processes, list):
448
413
  all_processes = [all_processes]
@@ -457,13 +422,7 @@ class WTLocalManager:
457
422
  if session_name in window_title or not window_title:
458
423
  session_windows.append(proc)
459
424
 
460
- active_sessions.append({
461
- "session_name": session_name,
462
- "is_active": len(session_windows) > 0,
463
- "tab_count": len(manager.tab_config),
464
- "tabs": list(manager.tab_config.keys()),
465
- "windows": session_windows
466
- })
425
+ active_sessions.append({"session_name": session_name, "is_active": len(session_windows) > 0, "tab_count": len(manager.tab_config), "tabs": list(manager.tab_config.keys()), "windows": session_windows})
467
426
 
468
427
  except Exception as e:
469
428
  logger.error(f"Error listing active sessions: {e}")
@@ -474,59 +433,29 @@ class WTLocalManager:
474
433
  """Get overview of all Windows Terminal windows and processes."""
475
434
  try:
476
435
  result = subprocess.run(
477
- ['powershell', '-Command',
478
- 'Get-Process -Name "WindowsTerminal" -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, MainWindowTitle, CPU | ConvertTo-Json -Depth 2'],
479
- capture_output=True,
480
- text=True,
481
- timeout=10
436
+ ["powershell", "-Command", 'Get-Process -Name "WindowsTerminal" -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, MainWindowTitle, CPU | ConvertTo-Json -Depth 2'], capture_output=True, text=True, timeout=10
482
437
  )
483
438
 
484
439
  if result.returncode == 0 and result.stdout.strip():
485
440
  import json
441
+
486
442
  processes = json.loads(result.stdout.strip())
487
443
  if not isinstance(processes, list):
488
444
  processes = [processes]
489
445
 
490
- return {
491
- "success": True,
492
- "total_windows": len(processes),
493
- "windows": processes,
494
- "managed_sessions": len(self.managers)
495
- }
446
+ return {"success": True, "total_windows": len(processes), "windows": processes, "managed_sessions": len(self.managers)}
496
447
  else:
497
- return {
498
- "success": True,
499
- "total_windows": 0,
500
- "windows": [],
501
- "managed_sessions": len(self.managers),
502
- "message": "No Windows Terminal processes found"
503
- }
448
+ return {"success": True, "total_windows": 0, "windows": [], "managed_sessions": len(self.managers), "message": "No Windows Terminal processes found"}
504
449
  except Exception as e:
505
- return {
506
- "success": False,
507
- "error": str(e),
508
- "managed_sessions": len(self.managers)
509
- }
450
+ return {"success": False, "error": str(e), "managed_sessions": len(self.managers)}
510
451
 
511
452
 
512
453
  if __name__ == "__main__":
513
454
  # Example usage
514
455
  sample_sessions = {
515
- "development": {
516
- "🚀Frontend": ("~/code/myapp/frontend", "npm run dev"),
517
- "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"),
518
- "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10")
519
- },
520
- "testing": {
521
- "🧪Tests": ("~/code/myapp", "pytest --watch"),
522
- "🔍Coverage": ("~/code/myapp", "python -m coverage run --source=. -m pytest"),
523
- "📝Logs": ("~/logs", "Get-Content app.log -Wait")
524
- },
525
- "deployment": {
526
- "🐳Docker": ("~/code/myapp", "docker-compose up"),
527
- "☸️K8s": ("~/k8s", "kubectl get pods --watch"),
528
- "📈Metrics": ("~", "Get-Counter \"\\Processor(_Total)\\% Processor Time\" -SampleInterval 2 -MaxSamples 30")
529
- }
456
+ "development": {"🚀Frontend": ("~/code/myapp/frontend", "npm run dev"), "⚙️Backend": ("~/code/myapp/backend", "python manage.py runserver"), "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10")},
457
+ "testing": {"🧪Tests": ("~/code/myapp", "pytest --watch"), "🔍Coverage": ("~/code/myapp", "python -m coverage run --source=. -m pytest"), "📝Logs": ("~/logs", "Get-Content app.log -Wait")},
458
+ "deployment": {"🐳Docker": ("~/code/myapp", "docker-compose up"), "☸️K8s": ("~/k8s", "kubectl get pods --watch"), "📈Metrics": ("~", 'Get-Counter "\\Processor(_Total)\\% Processor Time" -SampleInterval 2 -MaxSamples 30')},
530
459
  }
531
460
 
532
461
  try:
@@ -574,4 +503,5 @@ if __name__ == "__main__":
574
503
  except Exception as e:
575
504
  print(f"❌ Error: {e}")
576
505
  import traceback
577
- traceback.print_exc()
506
+
507
+ traceback.print_exc()
@@ -18,7 +18,6 @@ TMP_LAYOUT_DIR = Path.home().joinpath("tmp_results", "wt_layouts", "layout_manag
18
18
 
19
19
 
20
20
  class WTRemoteLayoutGenerator:
21
-
22
21
  def __init__(self, remote_name: str, session_name_prefix: str):
23
22
  self.remote_name = remote_name
24
23
  self.session_name = session_name_prefix + "_" + WTLayoutGenerator.generate_random_suffix()
@@ -102,14 +101,7 @@ class WTRemoteLayoutGenerator:
102
101
  return self.remote_executor.get_remote_windows_info()
103
102
 
104
103
  def to_dict(self) -> Dict[str, Any]:
105
- return {
106
- "remote_name": self.remote_name,
107
- "session_name": self.session_name,
108
- "tab_config": self.tab_config,
109
- "script_path": self.script_path,
110
- "created_at": datetime.now().isoformat(),
111
- "class_name": self.__class__.__name__
112
- }
104
+ return {"remote_name": self.remote_name, "session_name": self.session_name, "tab_config": self.tab_config, "script_path": self.script_path, "created_at": datetime.now().isoformat(), "class_name": self.__class__.__name__}
113
105
 
114
106
  def to_json(self, file_path: Optional[str] = None) -> str:
115
107
  # Generate file path if not provided
@@ -122,8 +114,8 @@ class WTRemoteLayoutGenerator:
122
114
  path_obj = Path(file_path)
123
115
 
124
116
  # Ensure .json extension
125
- if not str(path_obj).endswith('.json'):
126
- path_obj = path_obj.with_suffix('.json')
117
+ if not str(path_obj).endswith(".json"):
118
+ path_obj = path_obj.with_suffix(".json")
127
119
 
128
120
  # Ensure parent directory exists
129
121
  path_obj.parent.mkdir(parents=True, exist_ok=True)
@@ -138,38 +130,38 @@ class WTRemoteLayoutGenerator:
138
130
  return str(path_obj)
139
131
 
140
132
  @classmethod
141
- def from_json(cls, file_path: str) -> 'WTRemoteLayoutGenerator':
133
+ def from_json(cls, file_path: str) -> "WTRemoteLayoutGenerator":
142
134
  path_obj = Path(file_path)
143
135
 
144
136
  # Ensure .json extension
145
- if not str(path_obj).endswith('.json'):
146
- path_obj = path_obj.with_suffix('.json')
137
+ if not str(path_obj).endswith(".json"):
138
+ path_obj = path_obj.with_suffix(".json")
147
139
 
148
140
  if not path_obj.exists():
149
141
  raise FileNotFoundError(f"JSON file not found: {path_obj}")
150
142
 
151
143
  # Load JSON data
152
- with open(path_obj, 'r', encoding='utf-8') as f:
144
+ with open(path_obj, "r", encoding="utf-8") as f:
153
145
  data = json.load(f)
154
146
 
155
147
  # Validate that it's the correct class
156
- if data.get('class_name') != cls.__name__:
148
+ if data.get("class_name") != cls.__name__:
157
149
  logger.warning(f"Class name mismatch: expected {cls.__name__}, got {data.get('class_name')}")
158
150
 
159
151
  # Create new instance
160
152
  # Extract session name prefix by removing the suffix
161
- session_name = data['session_name']
162
- if '_' in session_name:
163
- session_name_prefix = '_'.join(session_name.split('_')[:-1])
153
+ session_name = data["session_name"]
154
+ if "_" in session_name:
155
+ session_name_prefix = "_".join(session_name.split("_")[:-1])
164
156
  else:
165
157
  session_name_prefix = session_name
166
158
 
167
- instance = cls(remote_name=data['remote_name'], session_name_prefix=session_name_prefix)
159
+ instance = cls(remote_name=data["remote_name"], session_name_prefix=session_name_prefix)
168
160
 
169
161
  # Restore state
170
- instance.session_name = data['session_name']
171
- instance.tab_config = data['tab_config']
172
- instance.script_path = data['script_path']
162
+ instance.session_name = data["session_name"]
163
+ instance.tab_config = data["tab_config"]
164
+ instance.script_path = data["script_path"]
173
165
 
174
166
  logger.info(f"✅ Loaded WTRemoteLayoutGenerator from: {file_path}")
175
167
  return instance
@@ -222,7 +214,7 @@ if __name__ == "__main__":
222
214
  "🤖Bot1": ("~/code/bytesense/bithence", "python bot1.py --create_new_bot True"),
223
215
  "🤖Bot2": ("~/code/bytesense/bithence", "python bot2.py --create_new_bot True"),
224
216
  "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10"),
225
- "📝Logs": ("C:/logs", "Get-Content app.log -Wait")
217
+ "📝Logs": ("C:/logs", "Get-Content app.log -Wait"),
226
218
  }
227
219
 
228
220
  # Replace 'myserver' with an actual SSH config alias for a Windows machine
@@ -267,7 +259,7 @@ if __name__ == "__main__":
267
259
  generator.print_status_report()
268
260
 
269
261
  # Show Windows Terminal overview
270
- print(f"\n🖥️ Windows Terminal Overview:")
262
+ print("\n🖥️ Windows Terminal Overview:")
271
263
  generator.print_windows_terminal_overview()
272
264
 
273
265
  # Start the session (uncomment to actually start)
@@ -277,12 +269,13 @@ if __name__ == "__main__":
277
269
  # Attach to session (uncomment to attach)
278
270
  # generator.attach_to_session()
279
271
 
280
- print(f"\n▶️ To start this session, run:")
281
- print(f" generator.start_wt_session()")
282
- print(f"\n📎 To attach to this session, run:")
283
- print(f" generator.attach_to_session()")
272
+ print("\n▶️ To start this session, run:")
273
+ print(" generator.start_wt_session()")
274
+ print("\n📎 To attach to this session, run:")
275
+ print(" generator.attach_to_session()")
284
276
 
285
277
  except Exception as e:
286
278
  print(f"❌ Error: {e}")
287
279
  import traceback
288
- traceback.print_exc()
280
+
281
+ traceback.print_exc()
@@ -40,10 +40,7 @@ class WTSessionManager:
40
40
 
41
41
  def kill_all_sessions(self) -> None:
42
42
  for an_m in self.managers:
43
- WTRemoteLayoutGenerator.run_remote_command(
44
- remote_name=an_m.remote_name,
45
- command="powershell -Command \"Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue | Stop-Process -Force\""
46
- )
43
+ WTRemoteLayoutGenerator.run_remote_command(remote_name=an_m.remote_name, command="powershell -Command \"Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue | Stop-Process -Force\"")
47
44
 
48
45
  def run_monitoring_routine(self, wait_ms: int = 60000) -> None:
49
46
  def routine(scheduler: Scheduler):
@@ -81,6 +78,7 @@ class WTSessionManager:
81
78
  # Print statuses
82
79
  for i, status in enumerate(statuses):
83
80
  print(f"Manager {i}: {status}")
81
+
84
82
  sched = Scheduler(routine=routine, wait_ms=wait_ms, logger=logger)
85
83
  sched.run()
86
84
 
@@ -98,13 +96,7 @@ class WTSessionManager:
98
96
  config_file.write_text(text, encoding="utf-8")
99
97
 
100
98
  # Save session metadata
101
- metadata = {
102
- "session_name_prefix": self.session_name_prefix,
103
- "created_at": str(datetime.now()),
104
- "num_managers": len(self.managers),
105
- "machines": list(self.machine2wt_tabs.keys()),
106
- "manager_type": "WTSessionManager"
107
- }
99
+ metadata = {"session_name_prefix": self.session_name_prefix, "created_at": str(datetime.now()), "num_managers": len(self.managers), "machines": list(self.machine2wt_tabs.keys()), "manager_type": "WTSessionManager"}
108
100
  metadata_file = session_dir / "metadata.json"
109
101
  text = json.dumps(metadata, indent=2, ensure_ascii=False)
110
102
  metadata_file.write_text(text, encoding="utf-8")
@@ -121,7 +113,7 @@ class WTSessionManager:
121
113
  return session_id
122
114
 
123
115
  @classmethod
124
- def load(cls, session_id: str) -> 'WTSessionManager':
116
+ def load(cls, session_id: str) -> "WTSessionManager":
125
117
  session_dir = TMP_SERIALIZATION_DIR / session_id
126
118
 
127
119
  if not session_dir.exists():
@@ -129,14 +121,14 @@ class WTSessionManager:
129
121
  config_file = session_dir / "machine2wt_tabs.json"
130
122
  if not config_file.exists():
131
123
  raise FileNotFoundError(f"Configuration file not found: {config_file}")
132
- with open(config_file, 'r', encoding='utf-8') as f:
124
+ with open(config_file, "r", encoding="utf-8") as f:
133
125
  machine2wt_tabs = json.load(f)
134
126
 
135
127
  # Load metadata
136
128
  metadata_file = session_dir / "metadata.json"
137
129
  session_name_prefix = "WTJobMgr" # default fallback
138
130
  if metadata_file.exists():
139
- with open(metadata_file, 'r', encoding='utf-8') as f:
131
+ with open(metadata_file, "r", encoding="utf-8") as f:
140
132
  metadata = json.load(f)
141
133
  session_name_prefix = metadata.get("session_name_prefix", "WTJobMgr")
142
134
  # Create new instance (this will create new managers)
@@ -179,6 +171,7 @@ class WTSessionManager:
179
171
 
180
172
  try:
181
173
  import shutil
174
+
182
175
  shutil.rmtree(session_dir)
183
176
  logger.info(f"✅ Deleted session: {session_id}")
184
177
  return True
@@ -205,10 +198,7 @@ class WTSessionManager:
205
198
  logger.error(f"❌ Failed to start session '{session_name}' on {remote_name}: {start_result.get('error')}")
206
199
 
207
200
  except Exception as e:
208
- results[f"{manager.remote_name}:{manager.session_name}"] = {
209
- "success": False,
210
- "error": str(e)
211
- }
201
+ results[f"{manager.remote_name}:{manager.session_name}"] = {"success": False, "error": str(e)}
212
202
  logger.error(f"❌ Exception starting session on {manager.remote_name}: {e}")
213
203
 
214
204
  return results
@@ -236,26 +226,11 @@ class WTSessionManager:
236
226
  "session_name": manager.session_name,
237
227
  "wt_status": wt_status,
238
228
  "commands_status": commands_status,
239
- "summary": {
240
- "total_commands": total_count,
241
- "running_commands": running_count,
242
- "stopped_commands": total_count - running_count,
243
- "session_healthy": wt_status.get("wt_running", False)
244
- }
229
+ "summary": {"total_commands": total_count, "running_commands": running_count, "stopped_commands": total_count - running_count, "session_healthy": wt_status.get("wt_running", False)},
245
230
  }
246
231
 
247
232
  except Exception as e:
248
- status_report[session_key] = {
249
- "remote_name": manager.remote_name,
250
- "session_name": manager.session_name,
251
- "error": str(e),
252
- "summary": {
253
- "total_commands": 0,
254
- "running_commands": 0,
255
- "stopped_commands": 0,
256
- "session_healthy": False
257
- }
258
- }
233
+ status_report[session_key] = {"remote_name": manager.remote_name, "session_name": manager.session_name, "error": str(e), "summary": {"total_commands": 0, "running_commands": 0, "stopped_commands": 0, "session_healthy": False}}
259
234
  logger.error(f"Error checking status for {session_key}: {e}")
260
235
 
261
236
  return status_report
@@ -265,12 +240,9 @@ class WTSessionManager:
265
240
  all_status = self.check_all_sessions_status()
266
241
 
267
242
  total_sessions = len(all_status)
268
- healthy_sessions = sum(1 for status in all_status.values()
269
- if status["summary"]["session_healthy"])
270
- total_commands = sum(status["summary"]["total_commands"]
271
- for status in all_status.values())
272
- total_running = sum(status["summary"]["running_commands"]
273
- for status in all_status.values())
243
+ healthy_sessions = sum(1 for status in all_status.values() if status["summary"]["session_healthy"])
244
+ total_commands = sum(status["summary"]["total_commands"] for status in all_status.values())
245
+ total_running = sum(status["summary"]["running_commands"] for status in all_status.values())
274
246
 
275
247
  return {
276
248
  "total_sessions": total_sessions,
@@ -281,7 +253,7 @@ class WTSessionManager:
281
253
  "stopped_commands": total_commands - total_running,
282
254
  "all_sessions_healthy": healthy_sessions == total_sessions,
283
255
  "all_commands_running": total_running == total_commands,
284
- "remote_machines": list(set(status["remote_name"] for status in all_status.values()))
256
+ "remote_machines": list(set(status["remote_name"] for status in all_status.values())),
285
257
  }
286
258
 
287
259
  def print_status_report(self) -> None:
@@ -366,19 +338,10 @@ class WTSessionManager:
366
338
  # Get Windows Terminal version
367
339
  wt_version = manager.get_wt_version()
368
340
 
369
- overview[remote_name] = {
370
- "windows_info": windows_info,
371
- "wt_processes": wt_processes,
372
- "wt_version": wt_version,
373
- "session_name": manager.session_name,
374
- "tab_count": len(manager.tab_config)
375
- }
341
+ overview[remote_name] = {"windows_info": windows_info, "wt_processes": wt_processes, "wt_version": wt_version, "session_name": manager.session_name, "tab_count": len(manager.tab_config)}
376
342
 
377
343
  except Exception as e:
378
- overview[manager.remote_name] = {
379
- "error": str(e),
380
- "session_name": manager.session_name
381
- }
344
+ overview[manager.remote_name] = {"error": str(e), "session_name": manager.session_name}
382
345
 
383
346
  return overview
384
347
 
@@ -431,14 +394,8 @@ class WTSessionManager:
431
394
  if __name__ == "__main__":
432
395
  # Example usage
433
396
  sample_machines = {
434
- "server1": {
435
- "🤖Bot1": ("~/code/project", "python bot1.py"),
436
- "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10"),
437
- },
438
- "server2": {
439
- "🤖Bot2": ("~/code/project", "python bot2.py"),
440
- "📝Logs": ("C:/logs", "Get-Content app.log -Wait"),
441
- }
397
+ "server1": {"🤖Bot1": ("~/code/project", "python bot1.py"), "📊Monitor": ("~", "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10")},
398
+ "server2": {"🤖Bot2": ("~/code/project", "python bot2.py"), "📝Logs": ("C:/logs", "Get-Content app.log -Wait")},
442
399
  }
443
400
 
444
401
  try:
@@ -483,4 +440,5 @@ if __name__ == "__main__":
483
440
  except Exception as e:
484
441
  print(f"❌ Error: {e}")
485
442
  import traceback
486
- traceback.print_exc()
443
+
444
+ traceback.print_exc()