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.
- machineconfig/cluster/cloud_manager.py +0 -3
- machineconfig/cluster/data_transfer.py +0 -1
- machineconfig/cluster/file_manager.py +0 -1
- machineconfig/cluster/job_params.py +0 -3
- machineconfig/cluster/loader_runner.py +0 -3
- machineconfig/cluster/remote_machine.py +0 -1
- machineconfig/cluster/script_notify_upon_completion.py +0 -1
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +3 -5
- machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
- machineconfig/cluster/sessions_managers/wt_local.py +36 -110
- machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
- machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
- machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
- machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
- machineconfig/cluster/templates/cli_click.py +0 -1
- machineconfig/cluster/templates/cli_gooey.py +0 -2
- machineconfig/cluster/templates/cli_trogon.py +0 -1
- machineconfig/cluster/templates/run_cloud.py +0 -1
- machineconfig/cluster/templates/run_cluster.py +0 -1
- machineconfig/cluster/templates/run_remote.py +0 -1
- machineconfig/cluster/templates/utils.py +26 -10
- machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
- machineconfig/jobs/python/check_installations.py +1 -0
- machineconfig/jobs/python/create_bootable_media.py +0 -2
- machineconfig/jobs/python/python_ve_symlink.py +9 -11
- machineconfig/jobs/python/tasks.py +0 -1
- machineconfig/jobs/python/vscode/api.py +5 -5
- machineconfig/jobs/python/vscode/link_ve.py +13 -14
- machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
- machineconfig/jobs/python/vscode/sync_code.py +9 -13
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
- machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
- machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
- machineconfig/jobs/python_custom_installers/dev/code.py +14 -21
- machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
- machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
- machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
- machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
- machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
- machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
- machineconfig/jobs/python_custom_installers/docker.py +12 -21
- machineconfig/jobs/python_custom_installers/gh.py +11 -19
- machineconfig/jobs/python_custom_installers/hx.py +32 -16
- machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
- machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
- machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
- machineconfig/jobs/windows/start_terminal.ps1 +1 -1
- machineconfig/profile/create.py +29 -22
- machineconfig/profile/create_hardlinks.py +26 -19
- machineconfig/profile/shell.py +51 -28
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/cloud/init.sh +2 -2
- machineconfig/scripts/linux/checkout_versions +1 -1
- machineconfig/scripts/linux/choose_wezterm_theme +1 -1
- machineconfig/scripts/linux/cloud_copy +1 -1
- machineconfig/scripts/linux/cloud_manager +1 -1
- machineconfig/scripts/linux/cloud_mount +1 -1
- machineconfig/scripts/linux/cloud_repo_sync +1 -1
- machineconfig/scripts/linux/cloud_sync +1 -1
- machineconfig/scripts/linux/croshell +1 -1
- machineconfig/scripts/linux/devops +4 -6
- machineconfig/scripts/linux/fire +1 -1
- machineconfig/scripts/linux/fire_agents +3 -2
- machineconfig/scripts/linux/ftpx +1 -1
- machineconfig/scripts/linux/gh_models +1 -1
- machineconfig/scripts/linux/kill_process +1 -1
- machineconfig/scripts/linux/mcinit +1 -1
- machineconfig/scripts/linux/repos +1 -1
- machineconfig/scripts/linux/scheduler +1 -1
- machineconfig/scripts/linux/start_slidev +1 -1
- machineconfig/scripts/linux/start_terminals +1 -1
- machineconfig/scripts/linux/url2md +1 -1
- machineconfig/scripts/linux/warp-cli.sh +122 -0
- machineconfig/scripts/linux/wifi_conn +1 -1
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__init__.py +0 -0
- machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/generate_files.py +84 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
- machineconfig/scripts/python/ai/mcinit.py +7 -3
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
- machineconfig/scripts/python/cloud_copy.py +1 -1
- machineconfig/scripts/python/cloud_mount.py +1 -1
- machineconfig/scripts/python/cloud_repo_sync.py +4 -4
- machineconfig/scripts/python/croshell.py +5 -3
- machineconfig/scripts/python/devops_add_identity.py +1 -1
- machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
- machineconfig/scripts/python/devops_backup_retrieve.py +1 -1
- machineconfig/scripts/python/devops_update_repos.py +140 -52
- machineconfig/scripts/python/dotfile.py +1 -1
- machineconfig/scripts/python/fire_agents.py +28 -9
- machineconfig/scripts/python/fire_jobs.py +3 -4
- machineconfig/scripts/python/ftpx.py +2 -1
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/helpers2.py +2 -2
- machineconfig/scripts/python/helpers/helpers4.py +1 -2
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/mount_nfs.py +1 -1
- machineconfig/scripts/python/mount_ssh.py +1 -1
- machineconfig/scripts/python/repos.py +1 -1
- machineconfig/scripts/python/start_slidev.py +1 -1
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/scripts/windows/checkout_version.ps1 +1 -3
- machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
- machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
- machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
- machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
- machineconfig/scripts/windows/croshell.ps1 +2 -2
- machineconfig/scripts/windows/devops.ps1 +1 -4
- machineconfig/scripts/windows/dotfile.ps1 +1 -3
- machineconfig/scripts/windows/fire.ps1 +1 -1
- machineconfig/scripts/windows/ftpx.ps1 +2 -2
- machineconfig/scripts/windows/gpt.ps1 +1 -1
- machineconfig/scripts/windows/kill_process.ps1 +1 -2
- machineconfig/scripts/windows/mcinit.ps1 +1 -1
- machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
- machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
- machineconfig/scripts/windows/pomodoro.ps1 +1 -1
- machineconfig/scripts/windows/py2exe.ps1 +1 -3
- machineconfig/scripts/windows/repos.ps1 +1 -1
- machineconfig/scripts/windows/scheduler.ps1 +1 -1
- machineconfig/scripts/windows/snapshot.ps1 +2 -2
- machineconfig/scripts/windows/start_slidev.ps1 +1 -1
- machineconfig/scripts/windows/start_terminals.ps1 +1 -1
- machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
- machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
- machineconfig/settings/lf/linux/lfrc +1 -1
- machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
- machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
- machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
- machineconfig/settings/svim/linux/init.toml +1 -1
- machineconfig/settings/svim/windows/init.toml +1 -1
- machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
- machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
- machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
- machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
- machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +66 -12
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +44 -36
- machineconfig/utils/ai/generate_file_checklist.py +8 -10
- machineconfig/utils/ai/url2md.py +4 -2
- machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
- machineconfig/utils/cloud/onedrive/transaction.py +63 -98
- machineconfig/utils/code.py +60 -39
- machineconfig/utils/installer.py +27 -33
- machineconfig/utils/installer_utils/installer_abc.py +8 -7
- machineconfig/utils/installer_utils/installer_class.py +149 -70
- machineconfig/utils/links.py +22 -11
- machineconfig/utils/notifications.py +197 -0
- machineconfig/utils/options.py +29 -23
- machineconfig/utils/path.py +13 -6
- machineconfig/utils/path_reduced.py +485 -216
- machineconfig/utils/procs.py +47 -41
- machineconfig/utils/scheduling.py +0 -1
- machineconfig/utils/ssh.py +157 -76
- machineconfig/utils/terminal.py +82 -37
- machineconfig/utils/utils.py +12 -10
- machineconfig/utils/utils2.py +38 -48
- machineconfig/utils/utils5.py +183 -116
- machineconfig/utils/ve.py +9 -4
- {machineconfig-2.0.dist-info â machineconfig-2.1.dist-info}/METADATA +3 -2
- {machineconfig-2.0.dist-info â machineconfig-2.1.dist-info}/RECORD +200 -217
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/archive/python_tools.txt +0 -12
- machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/update.py +0 -3
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/linux/activate_ve +0 -87
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/windows/activate_ve.ps1 +0 -54
- {machineconfig-2.0.dist-info â machineconfig-2.1.dist-info}/WHEEL +0 -0
- {machineconfig-2.0.dist-info â machineconfig-2.1.dist-info}/top_level.txt +0 -0
machineconfig/utils/utils5.py
CHANGED
|
@@ -1,31 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
from typing import Callable, Optional, Union, Any, NoReturn, TypeVar, Protocol, List
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Callable, Optional, Union, Any, NoReturn, TypeVar, Protocol, List, Generic
|
|
3
3
|
import logging
|
|
4
4
|
import time
|
|
5
5
|
from datetime import datetime, timezone, timedelta
|
|
6
|
+
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class LoggerTemplate(Protocol):
|
|
9
10
|
handlers: List[logging.Handler]
|
|
11
|
+
|
|
10
12
|
def debug(self, msg: str) -> None:
|
|
11
13
|
pass # 10
|
|
14
|
+
|
|
12
15
|
def info(self, msg: str) -> None:
|
|
13
16
|
pass # 20
|
|
17
|
+
|
|
14
18
|
def warning(self, msg: str) -> None:
|
|
15
19
|
pass # 30
|
|
20
|
+
|
|
16
21
|
def error(self, msg: str) -> None:
|
|
17
22
|
pass # 40
|
|
23
|
+
|
|
18
24
|
def critical(self, msg: str) -> None:
|
|
19
25
|
pass # 50
|
|
26
|
+
|
|
20
27
|
def fatal(self, msg: str) -> None:
|
|
21
28
|
pass # 50
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
class Scheduler:
|
|
25
|
-
def __init__(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
):
|
|
29
42
|
self.routine = routine # main routine to be repeated every `wait` time period
|
|
30
43
|
self.logger = logger
|
|
31
44
|
self.exception_handler = exception_handler if exception_handler is not None else self.default_exception_handler
|
|
@@ -35,8 +48,11 @@ class Scheduler:
|
|
|
35
48
|
self.max_cycles: int = max_cycles
|
|
36
49
|
self.sess_start_utc_ms: int
|
|
37
50
|
self.sess_stats = sess_stats or (lambda _sched: {})
|
|
38
|
-
|
|
39
|
-
def
|
|
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):
|
|
40
56
|
if max_cycles is not None:
|
|
41
57
|
self.max_cycles = max_cycles
|
|
42
58
|
if until_ms is None:
|
|
@@ -54,33 +70,46 @@ class Scheduler:
|
|
|
54
70
|
time2_ms = time.time_ns() // 1_000_000
|
|
55
71
|
time_left_ms = int(self.wait_ms - (time2_ms - time1_ms)) # 4- Conclude Message
|
|
56
72
|
self.cycle += 1
|
|
57
|
-
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)
|
|
58
|
-
try:
|
|
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
|
|
59
76
|
except KeyboardInterrupt as ex:
|
|
60
77
|
self.exception_handler(ex, "sleep", self)
|
|
61
78
|
return # that's probably the only kind of exception that can rise during sleep.
|
|
62
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})")
|
|
80
|
+
|
|
63
81
|
def get_records_df(self) -> List[dict[str, Any]]:
|
|
64
82
|
columns = ["start", "finish", "duration", "cycles", "termination reason"] + list(self.sess_stats(self).keys())
|
|
65
83
|
return [dict(zip(columns, row)) for row in self.records]
|
|
84
|
+
|
|
66
85
|
def record_session_end(self, reason: str):
|
|
67
86
|
end_time_ms = time.time_ns() // 1_000_000
|
|
68
87
|
duration_ms = end_time_ms - self.sess_start_utc_ms
|
|
69
88
|
sess_stats = self.sess_stats(self)
|
|
70
|
-
self.records.append(
|
|
71
|
-
|
|
72
|
-
|
|
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
|
+
)
|
|
73
100
|
records_df = self.get_records_df()
|
|
74
101
|
total_cycles = sum(row["cycles"] for row in records_df)
|
|
75
|
-
summ = {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
}
|
|
82
110
|
summ.update(sess_stats)
|
|
83
111
|
from machineconfig.utils.utils2 import get_repr
|
|
112
|
+
|
|
84
113
|
tmp = get_repr(summ)
|
|
85
114
|
self.logger.critical("\n--> Scheduler has finished running a session. \n" + tmp + "\n" + "-" * 100)
|
|
86
115
|
# Format records as table
|
|
@@ -105,109 +134,147 @@ class Scheduler:
|
|
|
105
134
|
table_str = "No records available."
|
|
106
135
|
self.logger.critical("\n--> Logger history.\n" + table_str)
|
|
107
136
|
return self
|
|
108
|
-
|
|
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.
|
|
109
139
|
print(sched)
|
|
110
140
|
self.record_session_end(reason=f"during {during}, " + str(ex))
|
|
111
141
|
self.logger.fatal(str(ex))
|
|
112
142
|
raise ex
|
|
113
143
|
|
|
114
144
|
|
|
115
|
-
T = TypeVar(
|
|
116
|
-
T2 = TypeVar(
|
|
145
|
+
T = TypeVar("T")
|
|
146
|
+
T2 = TypeVar("T2")
|
|
147
|
+
|
|
148
|
+
|
|
117
149
|
class PrintFunc(Protocol):
|
|
118
150
|
def __call__(self, msg: str) -> Union[NoReturn, None]: ...
|
|
119
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
|
+
|
|
120
160
|
|
|
121
|
-
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
#
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
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
|
|
@@ -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":
|
|
49
|
-
|
|
50
|
-
|
|
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: 2.
|
|
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
|
|
@@ -30,6 +30,7 @@ Requires-Dist: joblib>=1.5.2
|
|
|
30
30
|
Requires-Dist: randomname>=0.2.1
|
|
31
31
|
Requires-Dist: cryptography>=44.0.2
|
|
32
32
|
Requires-Dist: tenacity>=9.1.2
|
|
33
|
+
Requires-Dist: markdown>=3.9
|
|
33
34
|
Provides-Extra: windows
|
|
34
35
|
Requires-Dist: pywin32; extra == "windows"
|
|
35
36
|
Provides-Extra: docs
|
|
@@ -98,7 +99,7 @@ iwr https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/mac
|
|
|
98
99
|
(iwr bit.ly/cfgwt).Content | iex
|
|
99
100
|
. $HOME/code/machineconfig/src/machineconfig/setup_windows/symlinks.ps1
|
|
100
101
|
|
|
101
|
-
& "$HOME\
|
|
102
|
+
& "$HOME\code\machineconfig\.venv\Scripts\activate.ps1"
|
|
102
103
|
python -m fire machineconfig.profile.create main2 --choice=all
|
|
103
104
|
deactivate
|
|
104
105
|
|