machineconfig 7.57__py3-none-any.whl → 7.79__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 (104) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +21 -9
  2. machineconfig/jobs/installer/custom/boxes.py +2 -2
  3. machineconfig/jobs/installer/custom/hx.py +3 -3
  4. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  5. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  6. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
  7. machineconfig/jobs/installer/custom_dev/sysabc.py +36 -28
  8. machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
  9. machineconfig/jobs/installer/installer_data.json +127 -25
  10. machineconfig/jobs/installer/package_groups.py +20 -13
  11. machineconfig/profile/create_links_export.py +2 -2
  12. machineconfig/scripts/__init__.py +0 -4
  13. machineconfig/scripts/linux/wrap_mcfg +1 -1
  14. machineconfig/scripts/python/agents.py +22 -17
  15. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
  16. machineconfig/scripts/python/croshell.py +22 -17
  17. machineconfig/scripts/python/devops.py +3 -4
  18. machineconfig/scripts/python/devops_navigator.py +0 -4
  19. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  20. machineconfig/scripts/python/fire_jobs.py +19 -18
  21. machineconfig/scripts/python/ftpx.py +36 -12
  22. machineconfig/scripts/python/helpers/ast_search.py +74 -0
  23. machineconfig/scripts/python/helpers/qr_code.py +166 -0
  24. machineconfig/scripts/python/helpers/repo_rag.py +325 -0
  25. machineconfig/scripts/python/helpers/symantic_search.py +25 -0
  26. machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
  27. machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
  28. machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
  29. machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
  30. machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
  31. machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
  32. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
  33. machineconfig/scripts/python/helpers_devops/cli_nw.py +88 -7
  34. machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
  35. machineconfig/scripts/python/helpers_devops/cli_share_file.py +9 -9
  36. machineconfig/scripts/python/helpers_devops/cli_share_server.py +13 -12
  37. machineconfig/scripts/python/helpers_devops/cli_terminal.py +7 -6
  38. machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
  39. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  40. machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
  41. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
  42. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +23 -13
  43. machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
  44. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +7 -4
  45. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  46. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  47. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  48. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
  49. machineconfig/scripts/python/helpers_utils/download.py +152 -0
  50. machineconfig/scripts/python/helpers_utils/path.py +81 -31
  51. machineconfig/scripts/python/interactive.py +2 -2
  52. machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
  53. machineconfig/scripts/python/msearch.py +21 -2
  54. machineconfig/scripts/python/nw/address.py +132 -0
  55. machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
  56. machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
  57. machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
  58. machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
  59. machineconfig/scripts/python/sessions.py +35 -20
  60. machineconfig/scripts/python/terminal.py +2 -2
  61. machineconfig/scripts/python/utils.py +12 -10
  62. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  63. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  64. machineconfig/settings/shells/nushell/config.nu +2 -2
  65. machineconfig/settings/shells/nushell/env.nu +45 -6
  66. machineconfig/settings/shells/nushell/init.nu +282 -95
  67. machineconfig/settings/shells/pwsh/init.ps1 +1 -0
  68. machineconfig/settings/shells/zsh/init.sh +0 -7
  69. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  70. machineconfig/setup_windows/uv.ps1 +8 -1
  71. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  72. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
  73. machineconfig/utils/accessories.py +7 -4
  74. machineconfig/utils/code.py +6 -4
  75. machineconfig/utils/files/headers.py +2 -2
  76. machineconfig/utils/installer_utils/install_from_url.py +180 -0
  77. machineconfig/utils/installer_utils/installer_class.py +53 -47
  78. machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
  79. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
  80. machineconfig/utils/links.py +2 -2
  81. machineconfig/utils/meta.py +30 -16
  82. machineconfig/utils/options.py +4 -4
  83. machineconfig/utils/path_extended.py +3 -3
  84. machineconfig/utils/path_helper.py +33 -31
  85. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  86. machineconfig/utils/ssh.py +143 -409
  87. machineconfig/utils/ssh_utils/abc.py +8 -0
  88. machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
  89. machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
  90. machineconfig/utils/ssh_utils/utils.py +141 -0
  91. machineconfig/utils/ssh_utils/wsl.py +168 -0
  92. machineconfig/utils/upgrade_packages.py +2 -1
  93. machineconfig/utils/ve.py +11 -4
  94. {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/METADATA +1 -1
  95. {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/RECORD +102 -92
  96. {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/entry_points.txt +2 -2
  97. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  98. machineconfig/scripts/python/explore.py +0 -49
  99. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  100. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
  101. /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
  102. /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
  103. {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/WHEEL +0 -0
  104. {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/top_level.txt +0 -0
@@ -29,7 +29,8 @@ except (ImportError, ModuleNotFoundError) as ex:
29
29
  return txt
30
30
 
31
31
 
32
- def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_module: bool) -> str:
32
+ def lambda_to_python_script(lmb: Callable[[], Any],
33
+ in_global: bool, import_module: bool) -> str:
33
34
  """
34
35
  caveats: always use keyword arguments in the lambda call for best results.
35
36
  return statement not allowed in the wrapped function (otherwise it can be put in the global space)
@@ -55,6 +56,16 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
55
56
  import types as _types
56
57
  from pathlib import Path as _Path
57
58
 
59
+ def _stringify_annotation(annotation: Any) -> Any:
60
+ if annotation is _inspect.Signature.empty or annotation is _inspect.Parameter.empty:
61
+ return annotation
62
+ if isinstance(annotation, str):
63
+ return annotation
64
+ try:
65
+ return _inspect.formatannotation(annotation)
66
+ except Exception:
67
+ return str(annotation)
68
+
58
69
  # sanity checks
59
70
  if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
60
71
  raise TypeError("Expected a lambda function object")
@@ -174,16 +185,18 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
174
185
  else:
175
186
  new_default = param.default
176
187
 
177
- # Recreate the Parameter (keeping annotation and kind)
188
+ normalized_annotation = _stringify_annotation(param.annotation)
189
+
178
190
  if new_default is _inspect.Parameter.empty:
179
- new_param = _inspect.Parameter(name, param.kind, annotation=param.annotation)
191
+ new_param = _inspect.Parameter(name, param.kind, annotation=normalized_annotation)
180
192
  else:
181
193
  new_param = _inspect.Parameter(
182
- name, param.kind, default=new_default, annotation=param.annotation
194
+ name, param.kind, default=new_default, annotation=normalized_annotation
183
195
  )
184
196
  new_params.append(new_param)
185
197
 
186
- new_sig = _inspect.Signature(parameters=new_params, return_annotation=sig.return_annotation)
198
+ return_annotation = _stringify_annotation(sig.return_annotation)
199
+ new_sig = _inspect.Signature(parameters=new_params, return_annotation=return_annotation)
187
200
 
188
201
  # If in_global mode, return kwargs as global assignments + dedented body
189
202
  if in_global:
@@ -200,15 +213,11 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
200
213
 
201
214
  # Build type annotation string if available
202
215
  if param.annotation is not _inspect.Parameter.empty:
203
- # Try to get a nice string representation of the annotation
204
- try:
205
- if hasattr(param.annotation, "__name__"):
206
- type_str = param.annotation.__name__
207
- else:
208
- type_str = str(param.annotation)
209
- except Exception:
210
- type_str = str(param.annotation)
211
- global_assignments.append(f"{name}: {type_str} = {repr(value)}")
216
+ annotation_literal = _stringify_annotation(param.annotation)
217
+ if isinstance(annotation_literal, str):
218
+ global_assignments.append(f"{name}: {repr(annotation_literal)} = {repr(value)}")
219
+ else:
220
+ global_assignments.append(f"{name} = {repr(value)}")
212
221
  else:
213
222
  global_assignments.append(f"{name} = {repr(value)}")
214
223
 
@@ -233,10 +242,15 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
233
242
 
234
243
  if "Optional" in result_text or "Any" in result_text or "Union" in result_text or "Literal" in result_text:
235
244
  result_text = "from typing import Optional, Any, Union, Literal\n\n" + result_text
236
-
237
245
  if import_prefix:
238
246
  result_text = f"{import_prefix}{result_text}"
239
247
  return result_text
240
248
 
241
249
  if __name__ == "__main__":
242
- pass
250
+ from machineconfig.utils.code import print_code
251
+ import_code_robust = "<import_code_robust>"
252
+ res = lambda_to_python_script(
253
+ lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
254
+ in_global=True, import_module=False
255
+ )
256
+ print(res)
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
2
+ from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
3
3
  from rich.text import Text
4
4
  from rich.panel import Panel
5
5
  from rich.console import Console
@@ -14,10 +14,10 @@ from typing import Optional, Union, Iterable, overload, Literal, cast
14
14
 
15
15
 
16
16
  @overload
17
- def choose_from_options[T](msg: str, options: Iterable[T], multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False) -> T: ...
17
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False) -> T: ...
18
18
  @overload
19
- def choose_from_options[T](msg: str, options: Iterable[T], multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> list[T]: ...
20
- def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> Union[T, list[T]]:
19
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> list[T]: ...
20
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> Union[T, list[T]]:
21
21
  # TODO: replace with https://github.com/tmbo/questionary
22
22
  # # also see https://github.com/charmbracelet/gum
23
23
  options_strings: list[str] = [str(x) for x in options]
@@ -16,6 +16,7 @@ OPLike: TypeAlias = Union[str, "PathExtended", Path, None]
16
16
  PLike: TypeAlias = Union[str, "PathExtended", Path]
17
17
  FILE_MODE: TypeAlias = Literal["r", "w", "x", "a"]
18
18
  SHUTIL_FORMATS: TypeAlias = Literal["zip", "tar", "gztar", "bztar", "xztar"]
19
+ DECOMPRESS_SUPPORTED_FORMATS = [".tar.gz", ".tgz", ".tar", ".gz", ".tar.bz", ".tbz", ".tar.xz", ".zip", ".7z"]
19
20
 
20
21
 
21
22
  def _is_user_admin() -> bool:
@@ -152,7 +153,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
152
153
  # ======================================= File Editing / Reading ===================================
153
154
  def download(self, folder: OPLike = None, name: Optional[str] = None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> "PathExtended":
154
155
  import requests
155
-
156
156
  response = requests.get(self.as_url_str(), allow_redirects=allow_redirects, timeout=timeout, params=params) # Alternative: from urllib import request; request.urlopen(url).read().decode('utf-8').
157
157
  assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
158
158
  if name is not None:
@@ -711,7 +711,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
711
711
  :return: pathlib.Path pointing to the destination directory where contents were extracted
712
712
  :raises: FileNotFoundError if archive does not exist; py7zr.Bad7zFile or other error if extraction fails
713
713
  """
714
- import py7zr
714
+ import py7zr # type: ignore
715
715
  import tempfile
716
716
  from pathlib import Path
717
717
  archive_path_obj = Path(archive_path)
@@ -809,7 +809,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
809
809
  path = self
810
810
  else:
811
811
  try:
812
- path = self.rel2home()
812
+ path = PathExtended(self.expanduser().absolute().relative_to(Path.home()))
813
813
  except ValueError as ve:
814
814
  if strict:
815
815
  raise ve
@@ -1,4 +1,3 @@
1
- from machineconfig.utils.path_extended import PathExtended
2
1
  from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
3
2
  from rich.console import Console
4
3
  from rich.panel import Panel
@@ -10,8 +9,8 @@ from typing import Optional
10
9
  console = Console()
11
10
 
12
11
 
13
- def sanitize_path(a_path: str) -> PathExtended:
14
- path = PathExtended(a_path)
12
+ def sanitize_path(a_path: str) -> Path:
13
+ path = Path(a_path)
15
14
  if Path.cwd() == Path.home() and not path.exists():
16
15
  result = input("Current working directory is home, and passed path is not full path, are you sure you want to continue, [y]/n? ") or "y"
17
16
  if result == "y":
@@ -22,13 +21,13 @@ def sanitize_path(a_path: str) -> PathExtended:
22
21
  if platform.system() == "Windows": # path copied from Linux/Mac to Windows
23
22
  # For Linux: /home/username, for Mac: /Users/username
24
23
  skip_parts = 3 if path.as_posix().startswith("/home") else 3 # Both have 3 parts to skip
25
- path = PathExtended.home().joinpath(*path.parts[skip_parts:])
24
+ path = Path.home().joinpath(*path.parts[skip_parts:])
26
25
  assert path.exists(), f"File not found: {path}"
27
26
  source_os = "Linux" if path.as_posix().startswith("/home") else "macOS"
28
27
  console.print(Panel(f"🔗 PATH MAPPING | {source_os} → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
29
- elif platform.system() in ["Linux", "Darwin"] and PathExtended.home().as_posix() not in path.as_posix(): # copied between Unix-like systems with different username
28
+ elif platform.system() in ["Linux", "Darwin"] and Path.home().as_posix() not in path.as_posix(): # copied between Unix-like systems with different username
30
29
  skip_parts = 3 # Both /home/username and /Users/username have 3 parts to skip
31
- path = PathExtended.home().joinpath(*path.parts[skip_parts:])
30
+ path = Path.home().joinpath(*path.parts[skip_parts:])
32
31
  assert path.exists(), f"File not found: {path}"
33
32
  current_os = "Linux" if platform.system() == "Linux" else "macOS"
34
33
  source_os = "Linux" if path.as_posix().startswith("/home") else "macOS"
@@ -36,12 +35,12 @@ def sanitize_path(a_path: str) -> PathExtended:
36
35
  elif path.as_posix().startswith("C:"):
37
36
  if platform.system() in ["Linux", "Darwin"]: # path copied from Windows to Linux/Mac
38
37
  xx = str(a_path).replace("\\\\", "/")
39
- path = PathExtended.home().joinpath(*PathExtended(xx).parts[3:]) # exclude C:\\Users\\username
38
+ path = Path.home().joinpath(*Path(xx).parts[3:]) # exclude C:\\Users\\username
40
39
  assert path.exists(), f"File not found: {path}"
41
40
  target_os = "Linux" if platform.system() == "Linux" else "macOS"
42
41
  console.print(Panel(f"🔗 PATH MAPPING | Windows → {target_os}: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
43
- elif platform.system() == "Windows" and PathExtended.home().as_posix() not in path.as_posix(): # copied from Windows to Windows with different username
44
- path = PathExtended.home().joinpath(*path.parts[2:])
42
+ elif platform.system() == "Windows" and Path.home().as_posix() not in path.as_posix(): # copied from Windows to Windows with different username
43
+ path = Path.home().joinpath(*path.parts[2:])
45
44
  assert path.exists(), f"File not found: {path}"
46
45
  console.print(Panel(f"🔗 PATH MAPPING | Windows → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
47
46
  return path.expanduser().absolute()
@@ -66,12 +65,12 @@ def find_scripts(root: Path, name_substring: str, suffixes: set[str]) -> tuple[l
66
65
  return filename_matches, partial_path_matches
67
66
 
68
67
 
69
- def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[str]) -> PathExtended:
68
+ def match_file_name(sub_string: str, search_root: Path, suffixes: set[str]) -> Path:
70
69
  search_root_obj = search_root.absolute()
71
70
  # assume subscript is filename only, not a sub_path. There is no need to fzf over the paths.
72
71
  filename_matches, partial_path_matches = find_scripts(search_root_obj, sub_string, suffixes)
73
72
  if len(filename_matches) == 1:
74
- return PathExtended(filename_matches[0])
73
+ return Path(filename_matches[0])
75
74
  console.print(Panel(f"Partial filename {search_root_obj} match with case-insensitivity failed. This generated #{len(filename_matches)} results.", title="Search", expand=False))
76
75
  if len(filename_matches) < 20:
77
76
  print("\n".join([a_potential_match.as_posix() for a_potential_match in filename_matches]))
@@ -80,23 +79,23 @@ def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[st
80
79
  # let's see if avoiding .lower() helps narrowing down to one result
81
80
  reduced_scripts = [a_potential_match for a_potential_match in filename_matches if sub_string in a_potential_match.name]
82
81
  if len(reduced_scripts) == 1:
83
- return PathExtended(reduced_scripts[0])
82
+ return Path(reduced_scripts[0])
84
83
  elif len(reduced_scripts) > 1:
85
84
  from machineconfig.utils.options import choose_from_options
86
85
  choice = choose_from_options(multi=False, msg="Multiple matches found", options=reduced_scripts, fzf=True)
87
- return PathExtended(choice)
86
+ return Path(choice)
88
87
  print(f"Result: This still generated {len(reduced_scripts)} results.")
89
88
  if len(reduced_scripts) < 10:
90
89
  print("\n".join([a_potential_match.as_posix() for a_potential_match in reduced_scripts]))
91
90
 
92
91
  console.print(Panel(f"Partial path match with case-insensitivity failed. This generated #{len(partial_path_matches)} results.", title="Search", expand=False))
93
92
  if len(partial_path_matches) == 1:
94
- return PathExtended(partial_path_matches[0])
93
+ return Path(partial_path_matches[0])
95
94
  elif len(partial_path_matches) > 1:
96
95
  print("Try to narrow down partial_path_matches search by case-sensitivity.")
97
96
  reduced_scripts = [a_potential_match for a_potential_match in partial_path_matches if sub_string in a_potential_match.as_posix()]
98
97
  if len(reduced_scripts) == 1:
99
- return PathExtended(reduced_scripts[0])
98
+ return Path(reduced_scripts[0])
100
99
  print(f"Result: This still generated {len(reduced_scripts)} results.")
101
100
 
102
101
  try:
@@ -131,21 +130,24 @@ def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[st
131
130
  return search_root_obj.joinpath(res)
132
131
 
133
132
 
134
- def search_for_files_of_interest(path_obj: PathExtended, suffixes: set[str]):
135
- if path_obj.joinpath(".venv").exists():
136
- path_objects = path_obj.search("*", not_in=[".venv"])
137
- files: list[PathExtended] = []
138
- for a_path_obj in path_objects:
139
- files += search_for_files_of_interest(path_obj=a_path_obj, suffixes=suffixes)
140
- return files
133
+ def search_for_files_of_interest(path_obj: Path, suffixes: set[str]) -> list[Path]:
141
134
  if path_obj.is_file():
142
135
  return [path_obj]
143
- files: list[PathExtended] = []
144
- for a_suffix in suffixes:
145
- if a_suffix == ".py":
146
- files += path_obj.search(pattern="*.py", r=True, not_in=["__init__.py"])
147
- else:
148
- files += path_obj.search(pattern=f"*{a_suffix}", r=True)
136
+ files: list[Path] = []
137
+ directories_to_visit: list[Path] = [path_obj]
138
+ while directories_to_visit:
139
+ current_dir = directories_to_visit.pop()
140
+ for entry in current_dir.iterdir():
141
+ if entry.is_dir():
142
+ if entry.name == ".venv":
143
+ continue
144
+ directories_to_visit.append(entry)
145
+ continue
146
+ if entry.suffix not in suffixes:
147
+ continue
148
+ if entry.suffix == ".py" and entry.name == "__init__.py":
149
+ continue
150
+ files.append(entry)
149
151
  return files
150
152
 
151
153
 
@@ -160,15 +162,15 @@ def get_choice_file(path: str, suffixes: Optional[set[str]]):
160
162
  else:
161
163
  suffixes = {".py"}
162
164
  if not path_obj.exists():
163
- print(f"🔍 Searching for file matching `{path}` under `{PathExtended.cwd()}`, but only if suffix matches {suffixes}")
164
- choice_file = match_file_name(sub_string=path, search_root=PathExtended.cwd(), suffixes=suffixes)
165
+ print(f"🔍 Searching for file matching `{path}` under `{Path.cwd()}`, but only if suffix matches {suffixes}")
166
+ choice_file = match_file_name(sub_string=path, search_root=Path.cwd(), suffixes=suffixes)
165
167
  elif path_obj.is_dir():
166
168
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
167
169
  files = search_for_files_of_interest(path_obj, suffixes=suffixes)
168
170
  print(f"🔍 Got #{len(files)} results.")
169
171
  from machineconfig.utils.options import choose_from_options
170
172
  choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
171
- choice_file = PathExtended(choice_file)
173
+ choice_file = Path(choice_file)
172
174
  else:
173
175
  choice_file = path_obj
174
176
  return choice_file
@@ -13,7 +13,7 @@ class TabConfig(TypedDict):
13
13
  tabName: str
14
14
  startDir: str
15
15
  command: str
16
- tabWeight: NotRequired[int] # Optional, defaults to 1 if not provided
16
+ tabWeight: NotRequired[int]
17
17
 
18
18
 
19
19
  class LayoutConfig(TypedDict):