abstract-utilities 0.2.2.492__py3-none-any.whl → 0.2.2.583__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.
Files changed (201) hide show
  1. abstract_utilities/__init__.py +6 -10
  2. abstract_utilities/circular_import_finder.py +222 -0
  3. abstract_utilities/circular_import_finder2.py +118 -0
  4. abstract_utilities/class_utils/__init__.py +7 -0
  5. abstract_utilities/class_utils/abstract_classes.py +74 -0
  6. abstract_utilities/class_utils/caller_utils.py +53 -0
  7. abstract_utilities/class_utils/class_utils.py +109 -0
  8. abstract_utilities/class_utils/function_utils.py +153 -0
  9. abstract_utilities/class_utils/global_utils.py +57 -0
  10. abstract_utilities/class_utils/imports/__init__.py +2 -0
  11. abstract_utilities/class_utils/imports/imports.py +2 -0
  12. abstract_utilities/class_utils/imports/utils.py +40 -0
  13. abstract_utilities/class_utils/module_utils.py +63 -0
  14. abstract_utilities/directory_utils/__init__.py +2 -0
  15. abstract_utilities/directory_utils/directory_utils.py +94 -0
  16. abstract_utilities/directory_utils/imports/__init__.py +2 -0
  17. abstract_utilities/directory_utils/imports/imports.py +1 -0
  18. abstract_utilities/directory_utils/imports/module_imports.py +2 -0
  19. abstract_utilities/directory_utils/name_utils.py +43 -0
  20. abstract_utilities/directory_utils/size_utils.py +57 -0
  21. abstract_utilities/directory_utils/src/__init__.py +4 -0
  22. abstract_utilities/directory_utils/src/directory_utils.py +108 -0
  23. abstract_utilities/directory_utils/src/name_utils.py +43 -0
  24. abstract_utilities/directory_utils/src/size_utils.py +57 -0
  25. abstract_utilities/directory_utils/src/utils.py +116 -0
  26. abstract_utilities/directory_utils/utils.py +116 -0
  27. abstract_utilities/env_utils/imports/imports.py +5 -3
  28. abstract_utilities/error_utils/__init__.py +2 -0
  29. abstract_utilities/error_utils/error_utils.py +25 -0
  30. abstract_utilities/error_utils/imports/__init__.py +2 -0
  31. abstract_utilities/error_utils/imports/imports.py +1 -0
  32. abstract_utilities/error_utils/imports/module_imports.py +1 -0
  33. abstract_utilities/file_utils/__init__.py +1 -2
  34. abstract_utilities/file_utils/imports/constants.py +84 -4
  35. abstract_utilities/file_utils/imports/imports.py +2 -21
  36. abstract_utilities/file_utils/imports/module_imports.py +2 -7
  37. abstract_utilities/file_utils/module_imports.py +12 -0
  38. abstract_utilities/file_utils/src/__init__.py +7 -0
  39. abstract_utilities/file_utils/src/file_filters/__init__.py +4 -0
  40. abstract_utilities/file_utils/src/file_filters/ensure_utils.py +116 -0
  41. abstract_utilities/file_utils/src/file_filters/filter_params.py +86 -0
  42. abstract_utilities/file_utils/src/file_filters/filter_utils.py +78 -0
  43. abstract_utilities/file_utils/src/file_filters/predicate_utils.py +114 -0
  44. abstract_utilities/file_utils/src/file_filters.py +177 -0
  45. abstract_utilities/file_utils/src/file_reader.py +543 -0
  46. abstract_utilities/file_utils/src/file_utils.py +156 -0
  47. abstract_utilities/file_utils/src/filter_params.py +197 -0
  48. abstract_utilities/file_utils/src/find_collect.py +190 -0
  49. abstract_utilities/file_utils/src/find_content.py +210 -0
  50. abstract_utilities/file_utils/src/initFunctionsGen.py +280 -0
  51. abstract_utilities/file_utils/src/map_utils.py +29 -0
  52. abstract_utilities/file_utils/src/pdf_utils.py +300 -0
  53. abstract_utilities/file_utils/src/reader_utils/__init__.py +4 -0
  54. abstract_utilities/file_utils/src/reader_utils/directory_reader.py +53 -0
  55. abstract_utilities/file_utils/src/reader_utils/file_reader.py +543 -0
  56. abstract_utilities/file_utils/src/reader_utils/file_readers.py +376 -0
  57. abstract_utilities/file_utils/src/reader_utils/imports.py +18 -0
  58. abstract_utilities/file_utils/src/reader_utils/pdf_utils.py +300 -0
  59. abstract_utilities/file_utils/src/type_checks.py +91 -0
  60. abstract_utilities/file_utils (2)/__init__.py +2 -0
  61. abstract_utilities/file_utils (2)/imports/__init__.py +2 -0
  62. abstract_utilities/file_utils (2)/imports/constants.py +118 -0
  63. abstract_utilities/file_utils (2)/imports/imports/__init__.py +3 -0
  64. abstract_utilities/file_utils (2)/imports/imports/constants.py +119 -0
  65. abstract_utilities/file_utils (2)/imports/imports/imports.py +46 -0
  66. abstract_utilities/file_utils (2)/imports/imports/module_imports.py +8 -0
  67. abstract_utilities/file_utils (2)/imports/utils/__init__.py +3 -0
  68. abstract_utilities/file_utils (2)/imports/utils/classes.py +379 -0
  69. abstract_utilities/file_utils (2)/imports/utils/clean_imps.py +155 -0
  70. abstract_utilities/file_utils (2)/imports/utils/filter_utils.py +341 -0
  71. abstract_utilities/file_utils (2)/src/__init__.py +8 -0
  72. abstract_utilities/file_utils (2)/src/file_filters.py +155 -0
  73. abstract_utilities/file_utils (2)/src/file_reader.py +604 -0
  74. abstract_utilities/file_utils (2)/src/find_collect.py +258 -0
  75. abstract_utilities/file_utils (2)/src/initFunctionsGen.py +286 -0
  76. abstract_utilities/file_utils (2)/src/map_utils.py +28 -0
  77. abstract_utilities/file_utils (2)/src/pdf_utils.py +300 -0
  78. abstract_utilities/hash_utils/__init__.py +2 -0
  79. abstract_utilities/hash_utils/hash_utils.py +5 -0
  80. abstract_utilities/hash_utils/imports/__init__.py +2 -0
  81. abstract_utilities/hash_utils/imports/imports.py +1 -0
  82. abstract_utilities/hash_utils/imports/module_imports.py +0 -0
  83. abstract_utilities/history_utils/__init__.py +2 -0
  84. abstract_utilities/history_utils/history_utils.py +37 -0
  85. abstract_utilities/history_utils/imports/__init__.py +2 -0
  86. abstract_utilities/history_utils/imports/imports.py +1 -0
  87. abstract_utilities/history_utils/imports/module_imports.py +0 -0
  88. abstract_utilities/import_utils/__init__.py +2 -0
  89. abstract_utilities/import_utils/circular_import_finder.py +222 -0
  90. abstract_utilities/import_utils/circular_import_finder2.py +118 -0
  91. abstract_utilities/import_utils/imports/__init__.py +4 -0
  92. abstract_utilities/import_utils/imports/constants.py +2 -0
  93. abstract_utilities/import_utils/imports/imports.py +4 -0
  94. abstract_utilities/import_utils/imports/module_imports.py +8 -0
  95. abstract_utilities/import_utils/imports/utils.py +30 -0
  96. abstract_utilities/import_utils/src/__init__.py +7 -0
  97. abstract_utilities/import_utils/src/clean_imports.py +278 -0
  98. abstract_utilities/import_utils/src/dot_utils.py +80 -0
  99. abstract_utilities/import_utils/src/extract_utils.py +46 -0
  100. abstract_utilities/import_utils/src/import_functions.py +91 -0
  101. abstract_utilities/import_utils/src/import_utils.py +299 -0
  102. abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
  103. abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
  104. abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
  105. abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
  106. abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
  107. abstract_utilities/import_utils/src/package_utils.py +140 -0
  108. abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
  109. abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
  110. abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
  111. abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
  112. abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
  113. abstract_utilities/import_utils/src/pkg_utils.py +194 -0
  114. abstract_utilities/import_utils/src/sysroot_utils.py +112 -0
  115. abstract_utilities/imports.py +18 -0
  116. abstract_utilities/json_utils/__init__.py +2 -0
  117. abstract_utilities/json_utils/imports/__init__.py +2 -0
  118. abstract_utilities/json_utils/imports/imports.py +2 -0
  119. abstract_utilities/json_utils/imports/module_imports.py +5 -0
  120. abstract_utilities/json_utils/json_utils.py +743 -0
  121. abstract_utilities/list_utils/__init__.py +2 -0
  122. abstract_utilities/list_utils/imports/__init__.py +2 -0
  123. abstract_utilities/list_utils/imports/imports.py +1 -0
  124. abstract_utilities/list_utils/imports/module_imports.py +0 -0
  125. abstract_utilities/list_utils/list_utils.py +199 -0
  126. abstract_utilities/log_utils/__init__.py +5 -0
  127. abstract_utilities/log_utils/abstractLogManager.py +64 -0
  128. abstract_utilities/log_utils/call_response.py +68 -0
  129. abstract_utilities/log_utils/imports/__init__.py +2 -0
  130. abstract_utilities/log_utils/imports/imports.py +7 -0
  131. abstract_utilities/log_utils/imports/module_imports.py +2 -0
  132. abstract_utilities/log_utils/log_file.py +59 -0
  133. abstract_utilities/log_utils/logger_callable.py +49 -0
  134. abstract_utilities/math_utils/__init__.py +2 -0
  135. abstract_utilities/math_utils/imports/__init__.py +2 -0
  136. abstract_utilities/math_utils/imports/imports.py +2 -0
  137. abstract_utilities/math_utils/imports/module_imports.py +1 -0
  138. abstract_utilities/math_utils/math_utils.py +208 -0
  139. abstract_utilities/parse_utils/__init__.py +2 -0
  140. abstract_utilities/parse_utils/imports/__init__.py +3 -0
  141. abstract_utilities/parse_utils/imports/constants.py +10 -0
  142. abstract_utilities/parse_utils/imports/imports.py +2 -0
  143. abstract_utilities/parse_utils/imports/module_imports.py +4 -0
  144. abstract_utilities/parse_utils/parse_utils.py +516 -0
  145. abstract_utilities/path_utils/__init__.py +2 -0
  146. abstract_utilities/path_utils/imports/__init__.py +3 -0
  147. abstract_utilities/path_utils/imports/imports.py +1 -0
  148. abstract_utilities/path_utils/imports/module_imports.py +8 -0
  149. abstract_utilities/path_utils/path_utils.py +253 -0
  150. abstract_utilities/path_utils.py +95 -14
  151. abstract_utilities/read_write_utils/__init__.py +1 -0
  152. abstract_utilities/read_write_utils/imports/__init__.py +2 -0
  153. abstract_utilities/read_write_utils/imports/imports.py +2 -0
  154. abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
  155. abstract_utilities/read_write_utils/read_write_utils.py +338 -0
  156. abstract_utilities/read_write_utils.py +66 -34
  157. abstract_utilities/safe_utils/__init__.py +2 -0
  158. abstract_utilities/safe_utils/imports/__init__.py +3 -0
  159. abstract_utilities/safe_utils/imports/imports.py +2 -0
  160. abstract_utilities/safe_utils/imports/module_imports.py +2 -0
  161. abstract_utilities/safe_utils/safe_utils.py +166 -0
  162. abstract_utilities/ssh_utils/__init__.py +3 -1
  163. abstract_utilities/ssh_utils/classes.py +0 -1
  164. abstract_utilities/ssh_utils/cmd_utils.py +207 -0
  165. abstract_utilities/ssh_utils/imports/__init__.py +3 -0
  166. abstract_utilities/ssh_utils/imports/imports.py +5 -0
  167. abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
  168. abstract_utilities/ssh_utils/imports/utils.py +189 -0
  169. abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
  170. abstract_utilities/ssh_utils/type_checks.py +92 -0
  171. abstract_utilities/string_utils/__init__.py +4 -0
  172. abstract_utilities/string_utils/clean_utils.py +28 -0
  173. abstract_utilities/string_utils/eat_utils.py +103 -0
  174. abstract_utilities/string_utils/imports/__init__.py +3 -0
  175. abstract_utilities/string_utils/imports/imports.py +2 -0
  176. abstract_utilities/string_utils/imports/module_imports.py +2 -0
  177. abstract_utilities/string_utils/imports/utils.py +81 -0
  178. abstract_utilities/string_utils/replace_utils.py +27 -0
  179. abstract_utilities/thread_utils/__init__.py +2 -0
  180. abstract_utilities/thread_utils/imports/__init__.py +2 -0
  181. abstract_utilities/thread_utils/imports/imports.py +2 -0
  182. abstract_utilities/thread_utils/imports/module_imports.py +2 -0
  183. abstract_utilities/thread_utils/thread_utils.py +140 -0
  184. abstract_utilities/time_utils/__init__.py +2 -0
  185. abstract_utilities/time_utils/imports/__init__.py +2 -0
  186. abstract_utilities/time_utils/imports/imports.py +3 -0
  187. abstract_utilities/time_utils/imports/module_imports.py +1 -0
  188. abstract_utilities/time_utils/time_utils.py +392 -0
  189. abstract_utilities/type_utils/__init__.py +3 -0
  190. abstract_utilities/type_utils/alpha_utils.py +59 -0
  191. abstract_utilities/type_utils/imports/__init__.py +2 -0
  192. abstract_utilities/type_utils/imports/imports.py +4 -0
  193. abstract_utilities/type_utils/imports/module_imports.py +1 -0
  194. abstract_utilities/type_utils/num_utils.py +19 -0
  195. abstract_utilities/type_utils/type_utils.py +981 -0
  196. {abstract_utilities-0.2.2.492.dist-info → abstract_utilities-0.2.2.583.dist-info}/METADATA +1 -1
  197. abstract_utilities-0.2.2.583.dist-info/RECORD +277 -0
  198. imports/__init__.py +36 -0
  199. abstract_utilities-0.2.2.492.dist-info/RECORD +0 -92
  200. {abstract_utilities-0.2.2.492.dist-info → abstract_utilities-0.2.2.583.dist-info}/WHEEL +0 -0
  201. {abstract_utilities-0.2.2.492.dist-info → abstract_utilities-0.2.2.583.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,258 @@
1
+ from ..imports import *
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, List, Set
5
+
6
+ def get_proper_type_str(string):
7
+ if not string:
8
+ return None
9
+ string_lower = string.lower()
10
+ items = {
11
+ "d":["dir","dirs","directory","directories","d","dirname"],
12
+ "f":["file","filepath","file_path","files","filepaths","file_paths","f"]
13
+ }
14
+ for key,values in items.items():
15
+ if string_lower in values:
16
+ return key
17
+ init = string_lower[0] if len(string_lower)>0 else None
18
+ if init in items:
19
+ return init
20
+ def check_path_type(
21
+ path: str,
22
+ user: Optional[str] = None,
23
+ host: Optional[str] = None,
24
+ user_as_host: Optional[str] = None,
25
+ use_shell: bool = False
26
+ ) -> Literal["file", "directory", "missing", "unknown"]:
27
+ """
28
+ Determine whether a given path is a file, directory, or missing.
29
+ Works locally or remotely (via SSH).
30
+
31
+ Args:
32
+ path: The path to check.
33
+ user, host, user_as_host: SSH parameters if remote.
34
+ use_shell: Force shell test instead of Python os.path.
35
+ Returns:
36
+ One of: 'file', 'directory', 'missing', or 'unknown'
37
+ """
38
+
39
+ # --- remote check if user/host is given ---
40
+ if user_as_host or (user and host):
41
+ remote_target = user_as_host or f"{user}@{host}"
42
+ cmd = f"if [ -f '{path}' ]; then echo file; elif [ -d '{path}' ]; then echo directory; else echo missing; fi"
43
+ try:
44
+ result = subprocess.check_output(
45
+ ["ssh", remote_target, cmd],
46
+ stderr=subprocess.DEVNULL,
47
+ text=True,
48
+ timeout=5
49
+ ).strip()
50
+ return result if result in ("file", "directory", "missing") else "unknown"
51
+ except Exception:
52
+ return "unknown"
53
+
54
+ # --- local check ---
55
+ if not use_shell:
56
+ if os.path.isfile(path):
57
+ return "file"
58
+ elif os.path.isdir(path):
59
+ return "directory"
60
+ elif not os.path.exists(path):
61
+ return "missing"
62
+ return "unknown"
63
+ else:
64
+ # fallback using shell tests (useful for sandboxed contexts)
65
+ cmd = f"if [ -f '{path}' ]; then echo file; elif [ -d '{path}' ]; then echo directory; else echo missing; fi"
66
+ try:
67
+ output = subprocess.check_output(
68
+ cmd, shell=True, stderr=subprocess.DEVNULL, text=True
69
+ ).strip()
70
+ return output if output in ("file", "directory", "missing") else "unknown"
71
+ except Exception:
72
+ return "unknown"
73
+
74
+
75
+
76
+
77
+ def get_find_cmd(*args,
78
+ mindepth: Optional[int] = None,
79
+ maxdepth: Optional[int] = None,
80
+ depth: Optional[int] = None,
81
+ file_type: Optional[str] = None, # 'f' or 'd'
82
+ name: Optional[str] = None,
83
+ size: Optional[str] = None,
84
+ mtime: Optional[str] = None,
85
+ perm: Optional[str] = None,
86
+ user: Optional[str] = None,
87
+ **kwargs
88
+ ) -> str:
89
+ """
90
+ Construct a Unix `find` command string from cfg and explicit keyword args.
91
+
92
+ - Honors allowed/excluded patterns, dirs, and extensions from ScanConfig.
93
+ - Automatically applies mindepth/maxdepth/depth/file_type filters.
94
+ """
95
+
96
+ params = define_all_params(*args,**kwargs)
97
+ cmd = [f"find {shlex.quote(params.directories[0])}"]
98
+
99
+ # --- depth ---
100
+ if depth is not None:
101
+ cmd += [f"-mindepth {depth}", f"-maxdepth {depth}"]
102
+ else:
103
+ if mindepth is not None:
104
+ cmd.append(f"-mindepth {mindepth}")
105
+ if maxdepth is not None:
106
+ cmd.append(f"-maxdepth {maxdepth}")
107
+
108
+ # --- file type ---
109
+ if file_type in ("f", "d"):
110
+ cmd.append(f"-type {file_type}")
111
+
112
+ # --- base attributes ---
113
+ if name:
114
+ cmd.append(f"-name {shlex.quote(name)}")
115
+ if size:
116
+ cmd.append(f"-size {shlex.quote(size)}")
117
+ if mtime:
118
+ cmd.append(f"-mtime {shlex.quote(mtime)}")
119
+ if perm:
120
+ cmd.append(f"-perm {shlex.quote(perm)}")
121
+ if user:
122
+ cmd.append(f"-user {shlex.quote(user)}")
123
+ cfg = params.cfg
124
+ # --- cfg-based filters ---
125
+ if cfg:
126
+ # allowed extensions
127
+ if cfg.allowed_exts and cfg.allowed_exts != {"*"}:
128
+ ext_expr = " -o ".join(
129
+ [f"-name '*{e}'" for e in cfg.allowed_exts if e]
130
+ )
131
+ cmd.append(f"\\( {ext_expr} \\)")
132
+
133
+ # disallowed extensions
134
+ if cfg.exclude_exts:
135
+ for e in cfg.exclude_exts:
136
+ cmd.append(f"! -name '*{e}'")
137
+
138
+ # allowed directories (match any path)
139
+ if cfg.allowed_dirs and cfg.allowed_dirs != ["*"]:
140
+ dir_expr = " -o ".join(
141
+ [f"-path '*{d}*'" for d in cfg.allowed_dirs if d]
142
+ )
143
+ cmd.append(f"\\( {dir_expr} \\)")
144
+
145
+ # exclude directories
146
+ if cfg.exclude_dirs:
147
+ for d in cfg.exclude_dirs:
148
+ cmd.append(f"! -path '*{d}*'")
149
+
150
+ # allowed patterns
151
+ if cfg.allowed_patterns and cfg.allowed_patterns != ["*"]:
152
+ pat_expr = " -o ".join(
153
+ [f"-name '{p}'" for p in cfg.allowed_patterns if p]
154
+ )
155
+ cmd.append(f"\\( {pat_expr} \\)")
156
+
157
+ # exclude patterns
158
+ if cfg.exclude_patterns:
159
+ for p in cfg.exclude_patterns:
160
+ cmd.append(f"! -name '{p}'")
161
+
162
+ # allowed types
163
+ if cfg.allowed_types and cfg.allowed_types != {"*"}:
164
+ type_expr = " -o ".join(
165
+ [f"-path '*{t}*'" for t in cfg.allowed_types if t]
166
+ )
167
+ cmd.append(f"\\( {type_expr} \\)")
168
+
169
+ # excluded types
170
+ if cfg.exclude_types:
171
+ for t in cfg.exclude_types:
172
+ cmd.append(f"! -path '*{t}*'")
173
+
174
+ return " ".join(cmd)
175
+
176
+
177
+
178
+ def collect_globs(*args,
179
+ patterns: Optional[List[str]] = None,
180
+ mindepth: Optional[int] = None,
181
+ maxdepth: Optional[int] = None,
182
+ depth: Optional[int] = None,
183
+ file_type: Optional[str] = None, # "f", "d", or None
184
+ **kwargs
185
+ ) -> List[str] | dict:
186
+ """
187
+ Collect file or directory paths recursively.
188
+
189
+ - If file_type is None → returns {"f": [...], "d": [...]}
190
+ - If file_type is "f" or "d" → returns a list of that type
191
+ - Supports SSH mode via `user_at_host`
192
+ """
193
+ directories,cfg,allowed,include_files,recursive = get_file_filters(*args,**kwargs)
194
+
195
+ type_strs = {"f":"files","d":"dirs"}
196
+ file_type = get_proper_type_str(file_type)
197
+ file_types = make_list(file_type)
198
+ if file_type == None:
199
+ file_types = ["f","d"]
200
+ return_results = {}
201
+ return_result=[]
202
+ for file_type in file_types:
203
+ type_str = type_strs.get(file_type)
204
+ # Remote path (SSH)
205
+ for directory in directories:
206
+ find_cmd = get_find_cmd(
207
+ directory,
208
+ cfg=cfg,
209
+ mindepth=mindepth,
210
+ maxdepth=maxdepth,
211
+ depth=depth,
212
+ file_type=file_type,
213
+ **{k: v for k, v in kwargs.items() if v},
214
+ )
215
+ result = run_pruned_func(run_cmd,find_cmd,
216
+ **kwargs
217
+
218
+ )
219
+ return_result = [res for res in result.split('\n') if res]
220
+ if type_str not in return_results:
221
+ return_results[type_str]=[]
222
+ return_results[type_str]+=return_result
223
+ if len(file_types) == 1:
224
+ return return_result
225
+ return return_results
226
+
227
+ ## # Local path (Python-native walk)
228
+ ## root = Path(directory)
229
+ ##
230
+ ## results_js = {"f": [], "d": []}
231
+ ##
232
+ ## for p in root.rglob("*"):
233
+ ## if p.is_file():
234
+ ## kind = "f"
235
+ ## elif p.is_dir():
236
+ ## kind = "d"
237
+ ## else:
238
+ ## continue
239
+ ##
240
+ ## # If file_type is specified, skip the other kind
241
+ ## if file_type and kind != file_type:
242
+ ## continue
243
+ ##
244
+ ## if exts and kind == "f" and p.suffix.lower() not in exts:
245
+ ## continue
246
+ ##
247
+ ## if patterns and not any(p.match(pat) for pat in patterns):
248
+ ## continue
249
+ ##
250
+ ## results_js[kind].append(str(p))
251
+ ##
252
+ ## # Return based on selection
253
+ ## if file_type is None:
254
+ ## return results_js
255
+ ## else:
256
+ ## return results_js[file_type]
257
+
258
+
@@ -0,0 +1,286 @@
1
+ # attach_functions.py — single helper you can import anywhere
2
+ # attach_dynamic.py
3
+ from __future__ import annotations
4
+
5
+ from ..imports import *
6
+ ABSPATH = os.path.abspath(__file__)
7
+ ABSROOT = os.path.dirname(ABSPATH)
8
+ def caller_path():
9
+ frame = inspect.stack()[1]
10
+ return os.path.abspath(frame.filename)
11
+ def _is_defined_here(mod: types.ModuleType, obj: object) -> bool:
12
+ try:
13
+ return inspect.getmodule(obj) is mod
14
+ except Exception:
15
+ return False
16
+
17
+ def _collect_callables(mod: types.ModuleType) -> Dict[str, Callable]:
18
+ out: Dict[str, Callable] = {}
19
+ names = getattr(mod, "__all__", None)
20
+ if names:
21
+ # trust the author's export list
22
+ for n in names:
23
+ fn = getattr(mod, n, None)
24
+ if callable(fn):
25
+ out[n] = fn
26
+ return out
27
+ # otherwise, discover top-level callables defined in this module
28
+ for n in dir(mod):
29
+ if n.startswith("_"):
30
+ continue
31
+ obj = getattr(mod, n, None)
32
+ if callable(obj) and _is_defined_here(mod, obj):
33
+ out[n] = obj
34
+ return out
35
+
36
+ def _import_module_by_name(name: str) -> Optional[types.ModuleType]:
37
+ try:
38
+ return importlib.import_module(name)
39
+ except Exception:
40
+ return None
41
+
42
+ def _import_module_by_path(pkg_name: str, base_dir: str, filename: str) -> Optional[types.ModuleType]:
43
+ mod_name = f"{pkg_name}.functions"
44
+ path = os.path.join(base_dir, filename)
45
+ spec = importlib.util.spec_from_file_location(mod_name, path)
46
+ if not spec or not spec.loader:
47
+ return None
48
+ mod = importlib.util.module_from_spec(spec)
49
+ sys.modules[mod_name] = mod
50
+ spec.loader.exec_module(mod)
51
+ return mod
52
+
53
+ def _walk_functions_package(pkg_name: str, pkg_mod: types.ModuleType) -> List[types.ModuleType]:
54
+ """Import all immediate submodules in the functions/ package."""
55
+ mods: List[types.ModuleType] = [pkg_mod]
56
+ pkg_dir = os.path.dirname(pkg_mod.__file__ or "")
57
+ for info in pkgutil.iter_modules([pkg_dir]):
58
+ # only import direct children (no recursion here; easy to add if you need)
59
+ child_name = f"{pkg_mod.__name__}.{info.name}"
60
+ m = _import_module_by_name(child_name)
61
+ if m:
62
+ mods.append(m)
63
+ return mods
64
+
65
+ def _discover_functions(base_pkg: str, *, hot_reload: bool) -> List[Tuple[str, Callable, str]]:
66
+ """
67
+ Returns a list of (export_name, callable, module_basename).
68
+ Works if you have base_pkg.functions.py or base_pkg/functions/ package.
69
+ """
70
+ # Prefer normal import of '<base_pkg>.functions'
71
+ fqn = f"{base_pkg}.functions"
72
+ mod = _import_module_by_name(fqn)
73
+
74
+ if mod is None:
75
+ # fallback: sibling functions.py, even without being a package
76
+ base = _import_module_by_name(base_pkg)
77
+ if not base or not getattr(base, "__file__", None):
78
+ return []
79
+ base_dir = os.path.dirname(base.__file__)
80
+ if os.path.isfile(os.path.join(base_dir, "functions.py")):
81
+ mod = _import_module_by_path(base_pkg, base_dir, "functions.py")
82
+ else:
83
+ return []
84
+
85
+ if hot_reload:
86
+ try:
87
+ mod = importlib.reload(mod) # type: ignore[arg-type]
88
+ except Exception:
89
+ pass
90
+
91
+ results: List[Tuple[str, Callable, str]] = []
92
+ modules: List[types.ModuleType]
93
+
94
+ if hasattr(mod, "__path__"): # it's a package: import children
95
+ modules = _walk_functions_package(base_pkg, mod)
96
+ else:
97
+ modules = [mod]
98
+
99
+ for m in modules:
100
+ exported = _collect_callables(m)
101
+ module_basename = m.__name__.split(".")[-1]
102
+ for name, fn in exported.items():
103
+ results.append((name, fn, module_basename))
104
+ return results
105
+
106
+ def attach_functions(
107
+ obj_or_cls,
108
+ base_pkg: str | None = None,
109
+ hot_reload: bool = True,
110
+ prefix_with_module: bool = False,
111
+ include_private: bool = True,
112
+ only_defined_here: bool = True, # don't attach stuff imported from elsewhere
113
+ ) -> list[str]:
114
+ """
115
+ Attach all free functions found in <base_pkg>.functions (module or package)
116
+ to the *class* of obj_or_cls. Returns the list of attached attribute names.
117
+ """
118
+ cls = obj_or_cls if inspect.isclass(obj_or_cls) else obj_or_cls.__class__
119
+ # Derive "<package>.functions" from the class's module unless you pass base_pkg
120
+ caller_mod = cls.__module__
121
+ pkg_root = (base_pkg or caller_mod.rsplit(".", 1)[0]).rstrip(".")
122
+ funcs_pkg_name = f"{pkg_root}.functions"
123
+
124
+ def _import(name: str) -> ModuleType | None:
125
+ try:
126
+ if hot_reload and name in sys.modules:
127
+ return importlib.reload(sys.modules[name])
128
+ return importlib.import_module(name)
129
+ except Exception:
130
+ return None
131
+
132
+ def _is_pkg(m: ModuleType) -> bool:
133
+ return hasattr(m, "__path__")
134
+
135
+ mod = _import(funcs_pkg_name)
136
+ if mod is None:
137
+ # Nothing to attach (no functions.py or functions/ next to your class)
138
+ setattr(cls, "_attached_functions", tuple())
139
+ return []
140
+
141
+ modules: list[ModuleType] = [mod]
142
+ if _is_pkg(mod):
143
+ # attach from every submodule under functions/
144
+ for it in pkgutil.iter_modules(mod.__path__):
145
+ sub = _import(f"{funcs_pkg_name}.{it.name}")
146
+ if sub is not None:
147
+ modules.append(sub)
148
+
149
+ attached: list[str] = []
150
+ for m in modules:
151
+ for name, obj in vars(m).items():
152
+ # only callables (skip classes), and keep them sane
153
+ if not callable(obj) or isinstance(obj, type):
154
+ continue
155
+ if only_defined_here and getattr(obj, "__module__", None) != m.__name__:
156
+ continue
157
+ if not include_private and name.startswith("_"):
158
+ continue
159
+ if name.startswith("__") and name.endswith("__"):
160
+ continue
161
+ attr = f"{m.__name__.rsplit('.', 1)[-1]}__{name}" if prefix_with_module else name
162
+ try:
163
+ setattr(cls, attr, obj) # set on CLASS → becomes bound method on instances
164
+ attached.append(attr)
165
+ except Exception:
166
+ # don't explode if one name collides; keep going
167
+ continue
168
+
169
+ # handy for debugging
170
+ try:
171
+ setattr(cls, "_attached_functions", tuple(attached))
172
+ except Exception:
173
+ pass
174
+ return attached
175
+
176
+
177
+
178
+
179
+ def isTab(item):
180
+ item_lower = item.lower()
181
+ for key in ['console','tab']:
182
+ if item_lower.endswith(key):
183
+ return True
184
+ return False
185
+ def get_dir(root,item):
186
+ if None in [root]:
187
+ return None
188
+ path = root
189
+ if item != None:
190
+ path = os.path.join(path,item)
191
+ return path
192
+ def isDir(root,item=None):
193
+ path = get_dir(root,item)
194
+ if path:
195
+ return os.path.isdir(path)
196
+ def check_dir_item(root,item=None):
197
+ return (item and isTab(item) and isDir(root,item))
198
+ def get_dirs(root = None):
199
+ root = root or ABSROOT
200
+ dirpaths = [get_dir(root,item) for item in os.listdir(root) if check_dir_item(root,item)]
201
+ return dirpaths
202
+ def ifFunctionsInFile(root):
203
+ items = [os.path.join(root, "functions"),os.path.join(root, "functions.py")]
204
+ for item in items:
205
+ if os.path.exists(item):
206
+ return item
207
+
208
+
209
+ def get_for_all_tabs(root = None):
210
+ root = root or caller_path()
211
+ if os.path.isfile(root):
212
+ root = os.path.dirname(root)
213
+ all_tabs = get_dirs(root = root)
214
+ for ROOT in all_tabs:
215
+ FUNCS_DIR = ifFunctionsInFile(ROOT)
216
+ if FUNCS_DIR == None:
217
+ for ROOT in get_dirs(root = ROOT):
218
+ apply_inits(ROOT)
219
+ else:
220
+ apply_inits(ROOT)
221
+
222
+
223
+ def apply_inits(ROOT):
224
+ FUNCS_DIR = ifFunctionsInFile(ROOT)
225
+
226
+
227
+ if_fun_dir = isDir(FUNCS_DIR)
228
+ if if_fun_dir != None:
229
+
230
+ if if_fun_dir:
231
+ CFG = define_defaults(allowed_exts='.py',
232
+ unallowed_exts = True,
233
+ exclude_types = True,
234
+ exclude_dirs = True,
235
+ exclude_patterns = True)
236
+ _,filepaths = get_files_and_dirs(FUNCS_DIR,cfg=CFG)
237
+
238
+ else:
239
+ filepaths = [FUNCS_DIR]
240
+
241
+ # Parse top-level def names
242
+ def extract_funcs(path: str):
243
+ funcs = []
244
+ for line in read_from_file(path).splitlines():
245
+ m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
246
+ if m:
247
+ funcs.append(m.group(1))
248
+ return funcs
249
+
250
+ # Build functions/__init__.py that re-exports all discovered functions
251
+ import_lines = []
252
+ all_funcs = []
253
+ for fp in filepaths:
254
+ module = os.path.splitext(os.path.basename(fp))[0]
255
+ funcs = extract_funcs(fp)
256
+ if funcs:
257
+ import_lines.append(f"from .{module} import ({', '.join(funcs)})")
258
+ all_funcs.extend(funcs)
259
+ if if_fun_dir:
260
+ functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
261
+ write_to_file(contents=functions_init, file_path=os.path.join(FUNCS_DIR, "__init__.py"))
262
+
263
+ # Prepare the tuple literal of function names for import + loop
264
+ uniq_funcs = sorted(set(all_funcs))
265
+ func_tuple = ", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
266
+
267
+ # Generate apiConsole/initFuncs.py using the safer setattr-loop
268
+ init_funcs_src = textwrap.dedent(f"""\
269
+
270
+
271
+ from .functions import ({func_tuple})
272
+
273
+ def initFuncs(self):
274
+ try:
275
+ for f in ({func_tuple}):
276
+ setattr(self, f.__name__, f)
277
+ except Exception as e:
278
+ logger.info(f"{{e}}")
279
+ return self
280
+ """)
281
+
282
+ write_to_file(contents=init_funcs_src, file_path=os.path.join(ROOT, "initFuncs.py"))
283
+
284
+ def call_for_all_tabs():
285
+ root = get_caller_dir(2)
286
+ get_for_all_tabs(root)
@@ -0,0 +1,28 @@
1
+ from ..imports import MIME_TYPES,make_list,os
2
+ def get_file_type(file_path,types=None,default=None):
3
+ mime_types = {}
4
+ if types:
5
+ types = make_list(types)
6
+ for typ in types:
7
+ mime_types[typ] = MIME_TYPES.get(typ)
8
+ else:
9
+ mime_types = MIME_TYPES
10
+
11
+ if os.path.isfile(file_path):
12
+ basename = os.path.basename(file_path)
13
+ filename,ext = os.path.splitext(basename)
14
+ for file_type,ext_values in mime_types.items():
15
+ if ext in ext_values:
16
+ return file_type
17
+ def get_file_map(directory,types=None,default=None):
18
+ if directory and os.path.isfile(directory):
19
+ directory = os.path.dirname(directory)
20
+ all_types = {}
21
+ files = get_globs(directory)
22
+ for file in files:
23
+ file_type = get_file_type(file,types=types,default=default)
24
+ if file_type:
25
+ if file_type not in all_types:
26
+ all_types[file_type] = []
27
+ all_types[file_type].append(file)
28
+ return all_types