machineconfig 5.17__py3-none-any.whl → 5.19__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 (53) hide show
  1. machineconfig/cluster/sessions_managers/wt_local.py +6 -1
  2. machineconfig/cluster/sessions_managers/wt_local_manager.py +4 -2
  3. machineconfig/cluster/sessions_managers/wt_remote_manager.py +4 -2
  4. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +4 -2
  5. machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
  6. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -1
  7. machineconfig/profile/create.py +108 -140
  8. machineconfig/profile/create_frontend.py +58 -0
  9. machineconfig/profile/shell.py +45 -9
  10. machineconfig/scripts/python/ai/solutions/_shared.py +9 -1
  11. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +1 -1
  12. machineconfig/scripts/python/ai/solutions/generic.py +12 -2
  13. machineconfig/scripts/python/count_lines_frontend.py +1 -1
  14. machineconfig/scripts/python/devops.py +90 -54
  15. machineconfig/scripts/python/dotfile.py +14 -8
  16. machineconfig/scripts/python/interactive.py +3 -21
  17. machineconfig/scripts/python/share_terminal.py +1 -1
  18. machineconfig/setup_linux/__init__.py +11 -0
  19. machineconfig/setup_linux/{openssh_all.sh → ssh/openssh_all.sh} +1 -0
  20. machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
  21. machineconfig/setup_windows/__init__.py +12 -0
  22. machineconfig/setup_windows/apps.ps1 +1 -0
  23. machineconfig/setup_windows/{openssh_all.ps1 → ssh/openssh_all.ps1} +5 -5
  24. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +1 -1
  25. machineconfig/utils/code.py +7 -4
  26. machineconfig/utils/files/dbms.py +355 -0
  27. machineconfig/utils/files/read.py +2 -2
  28. machineconfig/utils/installer.py +5 -5
  29. machineconfig/utils/links.py +128 -104
  30. machineconfig/utils/procs.py +4 -4
  31. machineconfig/utils/scheduler.py +10 -14
  32. {machineconfig-5.17.dist-info → machineconfig-5.19.dist-info}/METADATA +1 -1
  33. {machineconfig-5.17.dist-info → machineconfig-5.19.dist-info}/RECORD +41 -51
  34. machineconfig/scripts/windows/dotfile.ps1 +0 -1
  35. machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +0 -57
  36. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -11
  37. machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -52
  38. machineconfig/setup_windows/symlinks.ps1 +0 -5
  39. machineconfig/setup_windows/symlinks2linux.ps1 +0 -1
  40. machineconfig/setup_windows/web_shortcuts/all.ps1 +0 -18
  41. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +0 -36
  42. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +0 -16
  43. machineconfig/setup_windows/web_shortcuts/ssh.ps1 +0 -11
  44. machineconfig/setup_windows/wsl_refresh.ps1 +0 -8
  45. machineconfig/setup_windows/wt_and_pwsh.ps1 +0 -9
  46. /machineconfig/setup_linux/{openssh_wsl.sh → ssh/openssh_wsl.sh} +0 -0
  47. /machineconfig/setup_windows/{quirks.ps1 → others/power_options.ps1} +0 -0
  48. /machineconfig/setup_windows/{openssh-server.ps1 → ssh/openssh-server.ps1} +0 -0
  49. /machineconfig/setup_windows/{openssh-server_add-sshkey.ps1 → ssh/openssh-server_add-sshkey.ps1} +0 -0
  50. /machineconfig/setup_windows/{openssh-server_add_identity.ps1 → ssh/openssh-server_add_identity.ps1} +0 -0
  51. {machineconfig-5.17.dist-info → machineconfig-5.19.dist-info}/WHEEL +0 -0
  52. {machineconfig-5.17.dist-info → machineconfig-5.19.dist-info}/entry_points.txt +0 -0
  53. {machineconfig-5.17.dist-info → machineconfig-5.19.dist-info}/top_level.txt +0 -0
@@ -84,195 +84,219 @@ def build_links(target_paths: list[tuple[PLike, str]], repo_root: PLike):
84
84
  links_path.symlink_to(target=a_target_path)
85
85
 
86
86
 
87
- def symlink_func(this: PathExtended, to_this: PathExtended, prioritize_to_this: bool) -> SymlinkResult:
88
- """helper function. creates a symlink from `this` to `to_this`.
87
+ def symlink_map(config_file_default_path: PathExtended, self_managed_config_file_path: PathExtended,
88
+ on_conflict: Literal["throwError", "overwriteSelfManaged", "backupSelfManaged", "overwriteDefaultPath", "backupDefaultPath"]
89
+ ) -> SymlinkResult:
90
+ """helper function. creates a symlink from `config_file_default_path` to `self_managed_config_file_path`.
89
91
 
90
92
  Returns a dict with 'action' and 'details' keys describing what was done.
91
93
 
92
- this: exists AND to_this exists AND this is a symlink pointing to to_this ===> Resolution: AUTO: do nothing, already linked correctly.
93
- this: exists AND to_this exists AND this is a symlink pointing to somewhere else ===> Resolution: AUTO: delete this symlink, create symlink to to_this
94
- this: exists AND to_this exists AND this is a concrete path ===> Resolution: DANGER: If files are identical (same hash), delete `this` and create symlink to `to_this`. Otherwise, two options: 1) prioritize `this`: to_this is backed up as to_this.orig_<randstr()>, to_this is deleted, and symlink is created from this to to_this as normal; 2) prioritize `to_this`: `this` is backed up as this.orig_<randstr()>, `this` is deleted, and symlink is created from this to to_this as normal.
94
+ on_conflict strategies:
95
+ - throwError: Raise exception when files differ
96
+ - overwriteSelfManaged: Delete self_managed_config_file_path (self-managed), move config_file_default_path to self_managed_config_file_path, create symlink
97
+ - backupSelfManaged: Backup self_managed_config_file_path (self-managed), move config_file_default_path to self_managed_config_file_path, create symlink
98
+ - overwriteDefaultPath: Delete config_file_default_path (default path), create symlink to self_managed_config_file_path
99
+ - backupDefaultPath: Backup config_file_default_path (default path), create symlink to self_managed_config_file_path
95
100
 
96
- this: exists AND to_this doesn't exist AND this is a symlink pointing to somewhere else ===> Resolution: AUTO: delete this symlink, create symlink to to_this (touch to_this)
97
- this: exists AND to_this doesn't exist AND this is a symlink pointing to to_this ===> Resolution: AUTO: delete this symlink, create symlink to to_this (touch to_this)
98
- this: exists AND to_this doesn't exist AND this is a concrete path ===> Resolution: AUTO: move this to to_this, then create symlink from this to to_this.
99
-
100
- this: doesn't exist AND to_this exists ===> Resolution: AUTO: create link from this to to_this
101
- this: doesn't exist AND to_this doesn't exist ===> Resolution: AUTO: create link from this to to_this (touch to_this)
101
+ Note: `config_file_default_path` is the default system location, `self_managed_config_file_path` is the self-managed config location
102
102
 
103
103
  """
104
- this = PathExtended(this).expanduser().absolute()
105
- to_this = PathExtended(to_this).expanduser().absolute()
104
+ config_file_default_path = PathExtended(config_file_default_path).expanduser().absolute()
105
+ self_managed_config_file_path = PathExtended(self_managed_config_file_path).expanduser().absolute()
106
106
  action_taken = ""
107
107
  details = ""
108
108
 
109
109
  # Case analysis based on docstring
110
- if this.exists():
111
- if to_this.exists():
112
- if this.is_symlink():
110
+ if config_file_default_path.exists():
111
+ if self_managed_config_file_path.exists():
112
+ if config_file_default_path.is_symlink():
113
113
  # Check if symlink already points to correct target
114
114
  try:
115
- if this.readlink().resolve() == to_this.resolve():
116
- # Case: this exists AND to_this exists AND this is a symlink pointing to to_this
115
+ if config_file_default_path.readlink().resolve() == self_managed_config_file_path.resolve():
116
+ # Case: config_file_default_path exists AND self_managed_config_file_path exists AND config_file_default_path is a symlink pointing to self_managed_config_file_path
117
117
  action_taken = "already_linked"
118
118
  details = "Symlink already correctly points to target"
119
- console.print(Panel(f"✅ ALREADY LINKED | {this} ➡️ {to_this}", title="Already Linked", expand=False))
119
+ console.print(Panel(f"✅ ALREADY LINKED | {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Already Linked", expand=False))
120
120
  return {"action": action_taken, "details": details}
121
121
  else:
122
- # Case: this exists AND to_this exists AND this is a symlink pointing to somewhere else
122
+ # Case: config_file_default_path exists AND self_managed_config_file_path exists AND config_file_default_path is a symlink pointing to somewhere else
123
123
  action_taken = "relinking"
124
124
  details = "Updated existing symlink to point to new target"
125
- console.print(Panel(f"🔄 RELINKING | Updating symlink from {this} ➡️ {to_this}", title="Relinking", expand=False))
126
- this.delete(sure=True)
125
+ console.print(Panel(f"🔄 RELINKING | Updating symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Relinking", expand=False))
126
+ config_file_default_path.delete(sure=True)
127
127
  except OSError:
128
128
  # Broken symlink case
129
129
  action_taken = "fixing_broken_link"
130
130
  details = "Removed broken symlink and will create new one"
131
- console.print(Panel(f"🔄 FIXING BROKEN LINK | Fixing broken symlink from {this} ➡️ {to_this}", title="Fixing Broken Link", expand=False))
132
- this.delete(sure=True)
131
+ console.print(Panel(f"🔄 FIXING BROKEN LINK | Fixing broken symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Fixing Broken Link", expand=False))
132
+ config_file_default_path.delete(sure=True)
133
133
  else:
134
- # Case: this exists AND to_this exists AND this is a concrete path
135
- if files_are_identical(this, to_this):
134
+ # Case: config_file_default_path exists AND self_managed_config_file_path exists AND config_file_default_path is a concrete path
135
+ if files_are_identical(config_file_default_path, self_managed_config_file_path):
136
136
  # Files are identical, just delete this and create symlink
137
137
  action_taken = "identical_files"
138
138
  details = "Files identical, removed source and will create symlink"
139
- console.print(Panel(f"🔗 IDENTICAL FILES | Files are identical, deleting {this} and creating symlink to {to_this}", title="Identical Files", expand=False))
140
- this.delete(sure=True)
139
+ console.print(Panel(f"🔗 IDENTICAL FILES | Files are identical, deleting {config_file_default_path} and creating symlink to {self_managed_config_file_path}", title="Identical Files", expand=False))
140
+ config_file_default_path.delete(sure=True)
141
141
  else:
142
- # Files are different, use prioritization logic
143
- if prioritize_to_this:
144
- # prioritize `to_this`: `this` is backed up, `this` is deleted, symlink created
145
- backup_name = f"{this}.orig_{randstr()}"
146
- action_taken = "backing_up_source"
147
- details = f"Backed up source to {backup_name}, prioritizing target"
148
- console.print(Panel(f"📦 BACKING UP | Moving {this} to {backup_name}, prioritizing {to_this}", title="Backing Up", expand=False))
149
- this.move(path=backup_name)
150
- else:
151
- # prioritize `this`: to_this is backed up, to_this is deleted, this content moved to to_this location
152
- backup_name = f"{to_this}.orig_{randstr()}"
142
+ # Files are different, use on_conflict strategy
143
+ if on_conflict == "throwError":
144
+ raise RuntimeError(f"Conflict detected: {config_file_default_path} and {self_managed_config_file_path} both exist with different content")
145
+ elif on_conflict == "overwriteSelfManaged":
153
146
  action_taken = "backing_up_target"
154
- details = f"Backed up target to {backup_name}, prioritizing source"
155
- console.print(Panel(f"📦 BACKING UP | Moving {to_this} to {backup_name}, prioritizing {this}", title="Backing Up", expand=False))
156
- to_this.move(path=backup_name)
157
- this.move(path=to_this)
147
+ details = "Overwriting self-managed config, moving default path to self-managed location"
148
+ console.print(Panel(f"📦 OVERWRITE SELF-MANAGED | Deleting {self_managed_config_file_path}, moving {config_file_default_path} to {self_managed_config_file_path}", title="Overwrite Self-Managed", expand=False))
149
+ self_managed_config_file_path.delete(sure=True)
150
+ config_file_default_path.move(path=self_managed_config_file_path)
151
+ elif on_conflict == "backupSelfManaged":
152
+ backup_name = f"{self_managed_config_file_path}.orig_{randstr()}"
153
+ action_taken = "backing_up_target"
154
+ details = f"Backed up self-managed config to {backup_name}"
155
+ console.print(Panel(f"📦 BACKUP SELF-MANAGED | Moving {self_managed_config_file_path} to {backup_name}, moving {config_file_default_path} to {self_managed_config_file_path}", title="Backup Self-Managed", expand=False))
156
+ self_managed_config_file_path.move(path=backup_name)
157
+ config_file_default_path.move(path=self_managed_config_file_path)
158
+ elif on_conflict == "overwriteDefaultPath":
159
+ action_taken = "backing_up_source"
160
+ details = "Overwriting default path, creating symlink to self-managed config"
161
+ console.print(Panel(f"📦 OVERWRITE DEFAULT | Deleting {config_file_default_path}, creating symlink to {self_managed_config_file_path}", title="Overwrite Default", expand=False))
162
+ config_file_default_path.delete(sure=True)
163
+ elif on_conflict == "backupDefaultPath":
164
+ backup_name = f"{config_file_default_path}.orig_{randstr()}"
165
+ action_taken = "backing_up_source"
166
+ details = f"Backed up default path to {backup_name}"
167
+ console.print(Panel(f"📦 BACKUP DEFAULT | Moving {config_file_default_path} to {backup_name}, creating symlink to {self_managed_config_file_path}", title="Backup Default", expand=False))
168
+ config_file_default_path.move(path=backup_name)
158
169
  else:
159
- # to_this doesn't exist
160
- if this.is_symlink():
161
- # Case: this exists AND to_this doesn't exist AND this is a symlink (pointing anywhere)
170
+ # self_managed_config_file_path doesn't exist
171
+ if config_file_default_path.is_symlink():
172
+ # Case: config_file_default_path exists AND self_managed_config_file_path doesn't exist AND config_file_default_path is a symlink (pointing anywhere)
162
173
  action_taken = "relinking_to_new_target"
163
174
  details = "Removed existing symlink, will create target and new symlink"
164
- console.print(Panel(f"🔄 RELINKING | Updating symlink from {this} ➡️ {to_this}", title="Relinking", expand=False))
165
- this.delete(sure=True)
166
- # Create to_this
167
- to_this.parent.mkdir(parents=True, exist_ok=True)
168
- to_this.touch()
175
+ console.print(Panel(f"🔄 RELINKING | Updating symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Relinking", expand=False))
176
+ config_file_default_path.delete(sure=True)
177
+ # Create self_managed_config_file_path
178
+ self_managed_config_file_path.parent.mkdir(parents=True, exist_ok=True)
179
+ self_managed_config_file_path.touch()
169
180
  else:
170
- # Case: this exists AND to_this doesn't exist AND this is a concrete path
181
+ # Case: config_file_default_path exists AND self_managed_config_file_path doesn't exist AND config_file_default_path is a concrete path
171
182
  action_taken = "moving_to_target"
172
183
  details = "Moved source to target location, will create symlink"
173
- console.print(Panel(f"📁 MOVING | Moving {this} to {to_this}, then creating symlink", title="Moving", expand=False))
174
- this.move(path=to_this)
184
+ console.print(Panel(f"📁 MOVING | Moving {config_file_default_path} to {self_managed_config_file_path}, then creating symlink", title="Moving", expand=False))
185
+ config_file_default_path.move(path=self_managed_config_file_path)
175
186
  else:
176
- # this doesn't exist
177
- if to_this.exists():
178
- # Case: this doesn't exist AND to_this exists
187
+ # config_file_default_path doesn't exist
188
+ if self_managed_config_file_path.exists():
189
+ # Case: config_file_default_path doesn't exist AND self_managed_config_file_path exists
179
190
  action_taken = "new_link"
180
191
  details = "Creating new symlink to existing target"
181
- console.print(Panel(f"🆕 NEW LINK | Creating new symlink from {this} ➡️ {to_this}", title="New Link", expand=False))
192
+ console.print(Panel(f"🆕 NEW LINK | Creating new symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="New Link", expand=False))
182
193
  else:
183
- # Case: this doesn't exist AND to_this doesn't exist
194
+ # Case: config_file_default_path doesn't exist AND self_managed_config_file_path doesn't exist
184
195
  action_taken = "new_link_and_target"
185
196
  details = "Creating target file and new symlink"
186
- console.print(Panel(f"🆕 NEW LINK & TARGET | Creating {to_this} and symlink from {this} ➡️ {to_this}", title="New Link & Target", expand=False))
187
- to_this.parent.mkdir(parents=True, exist_ok=True)
188
- to_this.touch()
197
+ console.print(Panel(f"🆕 NEW LINK & TARGET | Creating {self_managed_config_file_path} and symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="New Link & Target", expand=False))
198
+ self_managed_config_file_path.parent.mkdir(parents=True, exist_ok=True)
199
+ self_managed_config_file_path.touch()
189
200
 
190
201
  # Create the symlink
191
202
  try:
192
203
  action_taken = action_taken or "linking"
193
204
  details = details or "Creating symlink"
194
- console.print(Panel(f"🔗 LINKING | Creating symlink from {this} ➡️ {to_this}", title="Linking", expand=False))
195
- PathExtended(this).symlink_to(target=to_this, verbose=True, overwrite=True)
205
+ console.print(Panel(f"🔗 LINKING | Creating symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Linking", expand=False))
206
+ PathExtended(config_file_default_path).symlink_to(target=self_managed_config_file_path, verbose=True, overwrite=True)
196
207
  return {"action": action_taken, "details": details}
197
208
  except Exception as ex:
198
209
  action_taken = "error"
199
210
  details = f"Failed to create symlink: {str(ex)}"
200
- console.print(Panel(f"❌ ERROR | Failed at linking {this} ➡️ {to_this}. Reason: {ex}", title="Error", expand=False))
211
+ console.print(Panel(f"❌ ERROR | Failed at linking {config_file_default_path} ➡️ {self_managed_config_file_path}. Reason: {ex}", title="Error", expand=False))
201
212
  return {"action": action_taken, "details": details}
202
213
 
203
214
 
204
- def symlink_copy(this: PathExtended, to_this: PathExtended, prioritize_to_this: bool) -> CopyResult:
205
- this = PathExtended(this).expanduser().absolute()
206
- to_this = PathExtended(to_this).expanduser().absolute()
215
+ def copy_map(config_file_default_path: PathExtended, self_managed_config_file_path: PathExtended, on_conflict: Literal["throwError", "overwriteSelfManaged", "backupSelfManaged", "overwriteDefaultPath", "backupDefaultPath"]) -> CopyResult:
216
+ config_file_default_path = PathExtended(config_file_default_path).expanduser().absolute()
217
+ self_managed_config_file_path = PathExtended(self_managed_config_file_path).expanduser().absolute()
207
218
  action_taken = ""
208
219
  details = ""
209
220
 
210
- if this.exists():
211
- if to_this.exists():
212
- if this.is_symlink():
221
+ if config_file_default_path.exists():
222
+ if self_managed_config_file_path.exists():
223
+ if config_file_default_path.is_symlink():
213
224
  try:
214
- if this.readlink().resolve() == to_this.resolve():
225
+ if config_file_default_path.readlink().resolve() == self_managed_config_file_path.resolve():
215
226
  action_taken = "already_linked"
216
227
  details = "Symlink already correctly points to target"
217
- console.print(Panel(f"✅ ALREADY LINKED | {this} ➡️ {to_this}", title="Already Linked", expand=False))
228
+ console.print(Panel(f"✅ ALREADY LINKED | {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Already Linked", expand=False))
218
229
  return {"action": action_taken, "details": details}
219
230
  else:
220
231
  action_taken = "relinking"
221
232
  details = "Updated existing symlink to point to new target"
222
- console.print(Panel(f"🔄 RELINKING | Updating symlink from {this} ➡️ {to_this}", title="Relinking", expand=False))
223
- this.delete(sure=True)
233
+ console.print(Panel(f"🔄 RELINKING | Updating symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Relinking", expand=False))
234
+ config_file_default_path.delete(sure=True)
224
235
  except OSError:
225
236
  action_taken = "fixing_broken_link"
226
237
  details = "Removed broken symlink and will create new one"
227
- console.print(Panel(f"🔄 FIXING BROKEN LINK | Fixing broken symlink from {this} ➡️ {to_this}", title="Fixing Broken Link", expand=False))
228
- this.delete(sure=True)
238
+ console.print(Panel(f"🔄 FIXING BROKEN LINK | Fixing broken symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Fixing Broken Link", expand=False))
239
+ config_file_default_path.delete(sure=True)
229
240
  else:
230
- if prioritize_to_this:
231
- backup_name = f"{this}.orig_{randstr()}"
232
- action_taken = "backing_up_source"
233
- details = f"Backed up source to {backup_name}, prioritizing target"
234
- console.print(Panel(f"📦 BACKING UP | Moving {this} to {backup_name}, prioritizing {to_this}", title="Backing Up", expand=False))
235
- this.move(path=backup_name)
236
- else:
237
- backup_name = f"{to_this}.orig_{randstr()}"
241
+ if on_conflict == "throwError":
242
+ raise RuntimeError(f"Conflict detected: {config_file_default_path} and {self_managed_config_file_path} both exist with different content")
243
+ elif on_conflict == "overwriteSelfManaged":
244
+ action_taken = "backing_up_target"
245
+ details = "Overwriting self-managed config, moving default path to self-managed location"
246
+ console.print(Panel(f"📦 OVERWRITE SELF-MANAGED | Deleting {self_managed_config_file_path}, moving {config_file_default_path} to {self_managed_config_file_path}", title="Overwrite Self-Managed", expand=False))
247
+ self_managed_config_file_path.delete(sure=True)
248
+ config_file_default_path.move(path=self_managed_config_file_path)
249
+ elif on_conflict == "backupSelfManaged":
250
+ backup_name = f"{self_managed_config_file_path}.orig_{randstr()}"
238
251
  action_taken = "backing_up_target"
239
- details = f"Backed up target to {backup_name}, prioritizing source"
240
- console.print(Panel(f"📦 BACKING UP | Moving {to_this} to {backup_name}, prioritizing {this}", title="Backing Up", expand=False))
241
- to_this.move(path=backup_name)
242
- this.move(path=to_this)
252
+ details = f"Backed up self-managed config to {backup_name}"
253
+ console.print(Panel(f"📦 BACKUP SELF-MANAGED | Moving {self_managed_config_file_path} to {backup_name}, moving {config_file_default_path} to {self_managed_config_file_path}", title="Backup Self-Managed", expand=False))
254
+ self_managed_config_file_path.move(path=backup_name)
255
+ config_file_default_path.move(path=self_managed_config_file_path)
256
+ elif on_conflict == "overwriteDefaultPath":
257
+ action_taken = "backing_up_source"
258
+ details = "Overwriting default path, creating symlink to self-managed config"
259
+ console.print(Panel(f"📦 OVERWRITE DEFAULT | Deleting {config_file_default_path}, copying {self_managed_config_file_path}", title="Overwrite Default", expand=False))
260
+ config_file_default_path.delete(sure=True)
261
+ elif on_conflict == "backupDefaultPath":
262
+ backup_name = f"{config_file_default_path}.orig_{randstr()}"
263
+ action_taken = "backing_up_source"
264
+ details = f"Backed up default path to {backup_name}"
265
+ console.print(Panel(f"📦 BACKUP DEFAULT | Moving {config_file_default_path} to {backup_name}, copying {self_managed_config_file_path}", title="Backup Default", expand=False))
266
+ config_file_default_path.move(path=backup_name)
243
267
  else:
244
- if this.is_symlink():
268
+ if config_file_default_path.is_symlink():
245
269
  action_taken = "relinking_to_new_target"
246
270
  details = "Removed existing symlink, will create target and new symlink"
247
- console.print(Panel(f"🔄 RELINKING | Updating symlink from {this} ➡️ {to_this}", title="Relinking", expand=False))
248
- this.delete(sure=True)
249
- to_this.parent.mkdir(parents=True, exist_ok=True)
250
- to_this.touch()
271
+ console.print(Panel(f"🔄 RELINKING | Updating symlink from {config_file_default_path} ➡️ {self_managed_config_file_path}", title="Relinking", expand=False))
272
+ config_file_default_path.delete(sure=True)
273
+ self_managed_config_file_path.parent.mkdir(parents=True, exist_ok=True)
274
+ self_managed_config_file_path.touch()
251
275
  else:
252
276
  action_taken = "moving_to_target"
253
277
  details = "Moved source to target location, will copy"
254
- console.print(Panel(f"📁 MOVING | Moving {this} to {to_this}, then copying", title="Moving", expand=False))
255
- this.move(path=to_this)
278
+ console.print(Panel(f"📁 MOVING | Moving {config_file_default_path} to {self_managed_config_file_path}, then copying", title="Moving", expand=False))
279
+ config_file_default_path.move(path=self_managed_config_file_path)
256
280
  else:
257
- if to_this.exists():
281
+ if self_managed_config_file_path.exists():
258
282
  action_taken = "new_link"
259
283
  details = "Copying existing target to source location"
260
- console.print(Panel(f"🆕 NEW LINK | Copying {to_this} to {this}", title="New Link", expand=False))
284
+ console.print(Panel(f"🆕 NEW LINK | Copying {self_managed_config_file_path} to {config_file_default_path}", title="New Link", expand=False))
261
285
  else:
262
286
  action_taken = "new_link_and_target"
263
287
  details = "Creating target file and copying to source"
264
- console.print(Panel(f"🆕 NEW LINK & TARGET | Creating {to_this} and copying to {this}", title="New Link & Target", expand=False))
265
- to_this.parent.mkdir(parents=True, exist_ok=True)
266
- to_this.touch()
288
+ console.print(Panel(f"🆕 NEW LINK & TARGET | Creating {self_managed_config_file_path} and copying to {config_file_default_path}", title="New Link & Target", expand=False))
289
+ self_managed_config_file_path.parent.mkdir(parents=True, exist_ok=True)
290
+ self_managed_config_file_path.touch()
267
291
 
268
292
  try:
269
293
  action_taken = action_taken or "copying"
270
294
  details = details or "Copying file"
271
- console.print(Panel(f"📋 COPYING | Copying {to_this} to {this}", title="Copying", expand=False))
272
- to_this.copy(path=this, overwrite=True, verbose=True)
295
+ console.print(Panel(f"📋 COPYING | Copying {self_managed_config_file_path} to {config_file_default_path}", title="Copying", expand=False))
296
+ self_managed_config_file_path.copy(path=config_file_default_path, overwrite=True, verbose=True)
273
297
  return {"action": action_taken, "details": details}
274
298
  except Exception as ex:
275
299
  action_taken = "error"
276
300
  details = f"Failed to copy file: {str(ex)}"
277
- console.print(Panel(f"❌ ERROR | Failed at copying {to_this} to {this}. Reason: {ex}", title="Error", expand=False))
301
+ console.print(Panel(f"❌ ERROR | Failed at copying {self_managed_config_file_path} to {config_file_default_path}. Reason: {ex}", title="Error", expand=False))
278
302
  return {"action": action_taken, "details": details}
@@ -40,16 +40,16 @@ def get_processes_accessing_file(path: str):
40
40
 
41
41
 
42
42
  def kill_process(name: str):
43
- print(f"⚠️ Attempting to kill process: {name}...")
43
+ console.print(f"⚠️ Attempting to kill process: {name}...", style="yellow")
44
44
  killed = False
45
45
  for proc in psutil.process_iter():
46
46
  if proc.name() == name:
47
47
  proc.kill()
48
- print(f"💀 Process {name} (PID: {proc.pid}) terminated successfully")
48
+ console.print(f"💀 Process {name} (PID: {proc.pid}) terminated successfully", style="green")
49
49
  killed = True
50
50
  if not killed:
51
- print(f"❓ No process with name '{name}' was found")
52
- print(f"{'─' * 80}\n")
51
+ console.print(f"❓ No process with name '{name}' was found", style="red")
52
+ console.rule(style="dim")
53
53
 
54
54
 
55
55
  class ProcessManager:
@@ -1,5 +1,6 @@
1
+
1
2
  from pathlib import Path
2
- from typing import Callable, Optional, Union, Any, NoReturn, TypeVar, Protocol, List, Generic
3
+ from typing import Callable, Optional, Union, Any, TypeVar, Protocol, List, Generic
3
4
  import logging
4
5
  import time
5
6
  from datetime import datetime, timezone, timedelta
@@ -147,10 +148,6 @@ T = TypeVar("T")
147
148
  T2 = TypeVar("T2")
148
149
 
149
150
 
150
- class PrintFunc(Protocol):
151
- def __call__(self, msg: str) -> Union[NoReturn, None]: ...
152
-
153
-
154
151
  def to_pickle(obj: Any, path: Path) -> None:
155
152
  import pickle
156
153
 
@@ -159,9 +156,8 @@ def to_pickle(obj: Any, path: Path) -> None:
159
156
 
160
157
 
161
158
  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
159
  def __init__(
164
- self, source_func: Callable[[], T], expire: timedelta, logger: Optional[PrintFunc] = None, path: Optional[Path] = None, saver: Callable[[T, Path], Any] = to_pickle, reader: Callable[[Path], T] = from_pickle, name: Optional[str] = None
160
+ self, source_func: Callable[[], T], expire: timedelta, logger: LoggerTemplate, path: Optional[Path] = None, saver: Callable[[T, Path], Any] = to_pickle, reader: Callable[[Path], T] = from_pickle, name: Optional[str] = None
165
161
  ) -> None:
166
162
  self.cache: T
167
163
  self.source_func = source_func # function which when called returns a fresh object to be frozen.
@@ -209,7 +205,7 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
209
205
  🔍 Error: {ex}
210
206
  ════════════════════════════════════════════════════════
211
207
  """
212
- self.logger(msg1 + msg2)
208
+ self.logger.warning(msg1 + msg2)
213
209
  self.cache = self.source_func()
214
210
  self.last_call_is_fresh = True
215
211
  self.time_produced = datetime.now()
@@ -221,7 +217,7 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
221
217
  if self.logger:
222
218
  # Previous cache never existed or there was an explicit fresh order.
223
219
  why = "There was an explicit fresh order." if fresh else "Previous cache never existed or is corrupted."
224
- self.logger(f"""
220
+ self.logger.warning(f"""
225
221
  🆕 ════════════════════ NEW CACHE ════════════════════
226
222
  🔄 {self.name} cache: Populating fresh cache from source func
227
223
  ℹ️ Reason: {why}
@@ -239,7 +235,7 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
239
235
  return self(fresh=True)
240
236
  if age > self.expire:
241
237
  if self.logger:
242
- self.logger(f"""
238
+ self.logger.warning(f"""
243
239
  🔄 ════════════════════ CACHE UPDATE ════════════════════
244
240
  ⚠️ {self.name} cache: Updating cache from source func
245
241
  ⏱️ Age = {age} > {self.expire}
@@ -251,7 +247,7 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
251
247
  self.save(self.cache, self.path)
252
248
  else:
253
249
  if self.logger:
254
- self.logger(f"""
250
+ self.logger.warning(f"""
255
251
  ✅ ════════════════════ USING CACHE ════════════════════
256
252
  📦 {self.name} cache: Using cached values
257
253
  ⏱️ Lag = {age}
@@ -260,7 +256,7 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
260
256
 
261
257
  @staticmethod
262
258
  def as_decorator(
263
- expire: timedelta, logger: Optional[PrintFunc] = None, path: Optional[Path] = None, saver: Callable[[T2, Path], Any] = to_pickle, reader: Callable[[Path], T2] = from_pickle, name: Optional[str] = None
259
+ expire: timedelta, logger: LoggerTemplate, path: Optional[Path] = None, saver: Callable[[T2, Path], Any] = to_pickle, reader: Callable[[Path], T2] = from_pickle, name: Optional[str] = None
264
260
  ): # -> Callable[..., 'Cache[T2]']:
265
261
  def decorator(source_func: Callable[[], T2]) -> Cache["T2"]:
266
262
  res = Cache(source_func=source_func, expire=expire, logger=logger, path=path, name=name, reader=reader, saver=saver)
@@ -276,8 +272,8 @@ class Cache(Generic[T]): # This class helps to accelrate access to latest data
276
272
  returned_path = self.path.from_cloud(cloud=cloud, rel2home=rel2home, root=root)
277
273
  if returned_path is None and not exists:
278
274
  raise FileNotFoundError(f"❌ Failed to get @ {self.path}. Build the cache first with signed API.")
279
- elif returned_path is None and exists and self.logger is not None:
280
- self.logger(f"""
275
+ elif returned_path is None and exists:
276
+ self.logger.warning(f"""
281
277
  ⚠️ ════════════════════ CLOUD FETCH WARNING ════════════════════
282
278
  🔄 Failed to get fresh data from cloud
283
279
  📦 Using old cache @ {self.path}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: machineconfig
3
- Version: 5.17
3
+ Version: 5.19
4
4
  Summary: Dotfiles management package
5
5
  Author-email: Alex Al-Saffar <programmer@usa.com>
6
6
  License: Apache 2.0