abstract-utilities 0.2.2.480__py3-none-any.whl → 0.2.2.688__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 abstract-utilities might be problematic. Click here for more details.

Files changed (215) hide show
  1. abstract_utilities/__init__.py +24 -16
  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 +144 -0
  6. abstract_utilities/class_utils/caller_utils.py +92 -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 +71 -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 +110 -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/file_utils/type_checks.py +2 -1
  35. abstract_utilities/file_utils/imports/classes.py +59 -55
  36. abstract_utilities/file_utils/imports/constants.py +84 -4
  37. abstract_utilities/file_utils/imports/imports.py +2 -21
  38. abstract_utilities/file_utils/imports/module_imports.py +3 -8
  39. abstract_utilities/file_utils/module_imports.py +12 -0
  40. abstract_utilities/file_utils/src/__init__.py +7 -0
  41. abstract_utilities/file_utils/src/file_filters/__init__.py +1 -0
  42. abstract_utilities/file_utils/src/file_filters/ensure_utils.py +490 -0
  43. abstract_utilities/file_utils/src/file_filters/filter_params.py +150 -0
  44. abstract_utilities/file_utils/src/file_filters/filter_utils.py +78 -0
  45. abstract_utilities/file_utils/src/file_filters/predicate_utils.py +44 -0
  46. abstract_utilities/file_utils/src/file_filters.py +177 -0
  47. abstract_utilities/file_utils/src/file_reader.py +543 -0
  48. abstract_utilities/file_utils/src/file_utils.py +156 -0
  49. abstract_utilities/file_utils/src/filter_params.py +197 -0
  50. abstract_utilities/file_utils/src/find_collect.py +200 -0
  51. abstract_utilities/file_utils/src/find_content.py +210 -0
  52. abstract_utilities/file_utils/src/initFunctionsGen.py +293 -0
  53. abstract_utilities/file_utils/src/initFunctionsGens.py +280 -0
  54. abstract_utilities/file_utils/src/map_utils.py +29 -0
  55. abstract_utilities/file_utils/src/pdf_utils.py +300 -0
  56. abstract_utilities/file_utils/src/reader_utils/__init__.py +4 -0
  57. abstract_utilities/file_utils/src/reader_utils/directory_reader.py +53 -0
  58. abstract_utilities/file_utils/src/reader_utils/file_reader.py +543 -0
  59. abstract_utilities/file_utils/src/reader_utils/file_readers.py +376 -0
  60. abstract_utilities/file_utils/src/reader_utils/imports.py +18 -0
  61. abstract_utilities/file_utils/src/reader_utils/pdf_utils.py +300 -0
  62. abstract_utilities/file_utils/src/type_checks.py +91 -0
  63. abstract_utilities/file_utils (2)/__init__.py +2 -0
  64. abstract_utilities/file_utils (2)/imports/__init__.py +2 -0
  65. abstract_utilities/file_utils (2)/imports/constants.py +118 -0
  66. abstract_utilities/file_utils (2)/imports/imports/__init__.py +3 -0
  67. abstract_utilities/file_utils (2)/imports/imports/constants.py +119 -0
  68. abstract_utilities/file_utils (2)/imports/imports/imports.py +46 -0
  69. abstract_utilities/file_utils (2)/imports/imports/module_imports.py +8 -0
  70. abstract_utilities/file_utils (2)/imports/utils/__init__.py +3 -0
  71. abstract_utilities/file_utils (2)/imports/utils/classes.py +379 -0
  72. abstract_utilities/file_utils (2)/imports/utils/clean_imps.py +155 -0
  73. abstract_utilities/file_utils (2)/imports/utils/filter_utils.py +341 -0
  74. abstract_utilities/file_utils (2)/src/__init__.py +8 -0
  75. abstract_utilities/file_utils (2)/src/file_filters.py +155 -0
  76. abstract_utilities/file_utils (2)/src/file_reader.py +604 -0
  77. abstract_utilities/file_utils (2)/src/find_collect.py +258 -0
  78. abstract_utilities/file_utils (2)/src/initFunctionsGen.py +286 -0
  79. abstract_utilities/file_utils (2)/src/map_utils.py +28 -0
  80. abstract_utilities/file_utils (2)/src/pdf_utils.py +300 -0
  81. abstract_utilities/hash_utils/__init__.py +2 -0
  82. abstract_utilities/hash_utils/hash_utils.py +5 -0
  83. abstract_utilities/hash_utils/imports/__init__.py +2 -0
  84. abstract_utilities/hash_utils/imports/imports.py +1 -0
  85. abstract_utilities/hash_utils/imports/module_imports.py +0 -0
  86. abstract_utilities/history_utils/__init__.py +2 -0
  87. abstract_utilities/history_utils/history_utils.py +37 -0
  88. abstract_utilities/history_utils/imports/__init__.py +2 -0
  89. abstract_utilities/history_utils/imports/imports.py +1 -0
  90. abstract_utilities/history_utils/imports/module_imports.py +0 -0
  91. abstract_utilities/import_utils/__init__.py +2 -0
  92. abstract_utilities/import_utils/circular_import_finder.py +222 -0
  93. abstract_utilities/import_utils/circular_import_finder2.py +118 -0
  94. abstract_utilities/import_utils/imports/__init__.py +4 -0
  95. abstract_utilities/import_utils/imports/constants.py +2 -0
  96. abstract_utilities/import_utils/imports/imports.py +4 -0
  97. abstract_utilities/import_utils/imports/init_imports.py +3 -0
  98. abstract_utilities/import_utils/imports/module_imports.py +9 -0
  99. abstract_utilities/import_utils/imports/utils.py +30 -0
  100. abstract_utilities/import_utils/src/__init__.py +8 -0
  101. abstract_utilities/import_utils/src/clean_imports.py +278 -0
  102. abstract_utilities/import_utils/src/dot_utils.py +80 -0
  103. abstract_utilities/import_utils/src/extract_utils.py +46 -0
  104. abstract_utilities/import_utils/src/import_functions.py +110 -0
  105. abstract_utilities/import_utils/src/import_utils.py +349 -0
  106. abstract_utilities/import_utils/src/layze_import_utils/__init__.py +2 -0
  107. abstract_utilities/import_utils/src/layze_import_utils/lazy_utils.py +41 -0
  108. abstract_utilities/import_utils/src/layze_import_utils/nullProxy.py +37 -0
  109. abstract_utilities/import_utils/src/nullProxy.py +30 -0
  110. abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
  111. abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
  112. abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
  113. abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
  114. abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
  115. abstract_utilities/import_utils/src/package_utils.py +140 -0
  116. abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
  117. abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
  118. abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
  119. abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
  120. abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
  121. abstract_utilities/import_utils/src/pkg_utils.py +194 -0
  122. abstract_utilities/import_utils/src/sysroot_utils.py +112 -0
  123. abstract_utilities/imports.py +21 -0
  124. abstract_utilities/json_utils/__init__.py +2 -0
  125. abstract_utilities/json_utils/imports/__init__.py +2 -0
  126. abstract_utilities/json_utils/imports/imports.py +2 -0
  127. abstract_utilities/json_utils/imports/module_imports.py +5 -0
  128. abstract_utilities/json_utils/json_utils.py +777 -0
  129. abstract_utilities/list_utils/__init__.py +2 -0
  130. abstract_utilities/list_utils/imports/__init__.py +2 -0
  131. abstract_utilities/list_utils/imports/imports.py +1 -0
  132. abstract_utilities/list_utils/imports/module_imports.py +0 -0
  133. abstract_utilities/list_utils/list_utils.py +202 -0
  134. abstract_utilities/log_utils/__init__.py +5 -0
  135. abstract_utilities/log_utils/abstractLogManager.py +64 -0
  136. abstract_utilities/log_utils/call_response.py +68 -0
  137. abstract_utilities/log_utils/imports/__init__.py +2 -0
  138. abstract_utilities/log_utils/imports/imports.py +7 -0
  139. abstract_utilities/log_utils/imports/module_imports.py +2 -0
  140. abstract_utilities/log_utils/log_file.py +162 -0
  141. abstract_utilities/log_utils/logger_callable.py +49 -0
  142. abstract_utilities/math_utils/__init__.py +2 -0
  143. abstract_utilities/math_utils/imports/__init__.py +2 -0
  144. abstract_utilities/math_utils/imports/imports.py +2 -0
  145. abstract_utilities/math_utils/imports/module_imports.py +1 -0
  146. abstract_utilities/math_utils/math_utils.py +208 -0
  147. abstract_utilities/parse_utils/__init__.py +2 -0
  148. abstract_utilities/parse_utils/imports/__init__.py +3 -0
  149. abstract_utilities/parse_utils/imports/constants.py +10 -0
  150. abstract_utilities/parse_utils/imports/imports.py +2 -0
  151. abstract_utilities/parse_utils/imports/module_imports.py +4 -0
  152. abstract_utilities/parse_utils/parse_utils.py +539 -0
  153. abstract_utilities/path_utils/__init__.py +2 -0
  154. abstract_utilities/path_utils/imports/__init__.py +3 -0
  155. abstract_utilities/path_utils/imports/imports.py +1 -0
  156. abstract_utilities/path_utils/imports/module_imports.py +8 -0
  157. abstract_utilities/path_utils/path_utils.py +248 -0
  158. abstract_utilities/path_utils.py +95 -14
  159. abstract_utilities/read_write_utils/__init__.py +1 -0
  160. abstract_utilities/read_write_utils/imports/__init__.py +2 -0
  161. abstract_utilities/read_write_utils/imports/imports.py +2 -0
  162. abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
  163. abstract_utilities/read_write_utils/read_write_utils.py +439 -0
  164. abstract_utilities/read_write_utils.py +113 -62
  165. abstract_utilities/safe_utils/__init__.py +2 -0
  166. abstract_utilities/safe_utils/imports/__init__.py +3 -0
  167. abstract_utilities/safe_utils/imports/imports.py +2 -0
  168. abstract_utilities/safe_utils/imports/module_imports.py +2 -0
  169. abstract_utilities/safe_utils/safe_utils.py +166 -0
  170. abstract_utilities/ssh_utils/__init__.py +3 -1
  171. abstract_utilities/ssh_utils/classes.py +0 -1
  172. abstract_utilities/ssh_utils/cmd_utils.py +207 -0
  173. abstract_utilities/ssh_utils/imports/__init__.py +3 -0
  174. abstract_utilities/ssh_utils/imports/imports.py +5 -0
  175. abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
  176. abstract_utilities/ssh_utils/imports/utils.py +189 -0
  177. abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
  178. abstract_utilities/ssh_utils/type_checks.py +92 -0
  179. abstract_utilities/string_utils/__init__.py +4 -0
  180. abstract_utilities/string_utils/clean_utils.py +28 -0
  181. abstract_utilities/string_utils/eat_utils.py +103 -0
  182. abstract_utilities/string_utils/imports/__init__.py +3 -0
  183. abstract_utilities/string_utils/imports/imports.py +2 -0
  184. abstract_utilities/string_utils/imports/module_imports.py +2 -0
  185. abstract_utilities/string_utils/imports/utils.py +81 -0
  186. abstract_utilities/string_utils/replace_utils.py +27 -0
  187. abstract_utilities/string_utils.py +1 -1
  188. abstract_utilities/thread_utils/__init__.py +2 -0
  189. abstract_utilities/thread_utils/imports/__init__.py +2 -0
  190. abstract_utilities/thread_utils/imports/imports.py +2 -0
  191. abstract_utilities/thread_utils/imports/module_imports.py +2 -0
  192. abstract_utilities/thread_utils/thread_utils.py +140 -0
  193. abstract_utilities/time_utils/__init__.py +2 -0
  194. abstract_utilities/time_utils/imports/__init__.py +2 -0
  195. abstract_utilities/time_utils/imports/imports.py +3 -0
  196. abstract_utilities/time_utils/imports/module_imports.py +1 -0
  197. abstract_utilities/time_utils/time_utils.py +392 -0
  198. abstract_utilities/type_utils/__init__.py +7 -0
  199. abstract_utilities/type_utils/alpha_utils.py +59 -0
  200. abstract_utilities/type_utils/get_type.py +120 -0
  201. abstract_utilities/type_utils/imports/__init__.py +3 -0
  202. abstract_utilities/type_utils/imports/constants.py +134 -0
  203. abstract_utilities/type_utils/imports/imports.py +4 -0
  204. abstract_utilities/type_utils/imports/module_imports.py +25 -0
  205. abstract_utilities/type_utils/is_type.py +455 -0
  206. abstract_utilities/type_utils/make_type.py +126 -0
  207. abstract_utilities/type_utils/mime_types.py +68 -0
  208. abstract_utilities/type_utils/num_utils.py +19 -0
  209. abstract_utilities/type_utils/type_utils.py +104 -0
  210. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/METADATA +1 -1
  211. abstract_utilities-0.2.2.688.dist-info/RECORD +288 -0
  212. imports/__init__.py +36 -0
  213. abstract_utilities-0.2.2.480.dist-info/RECORD +0 -92
  214. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/WHEEL +0 -0
  215. {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,293 @@
1
+ # attach_functions.py — single helper you can import anywhere
2
+ # attach_dynamic.py
3
+ from __future__ import annotations
4
+ from .find_collect import get_files_and_dirs
5
+ from ..imports import *
6
+ ABSPATH = os.path.abspath(__file__)
7
+ ABSROOT = os.path.dirname(ABSPATH)
8
+
9
+ def caller_path():
10
+ frame = inspect.stack()[1]
11
+ return os.path.abspath(frame.filename)
12
+ def _is_defined_here(mod: types.ModuleType, obj: object) -> bool:
13
+ try:
14
+ return inspect.getmodule(obj) is mod
15
+ except Exception:
16
+ return False
17
+
18
+ def _collect_callables(mod: types.ModuleType) -> Dict[str, Callable]:
19
+ out: Dict[str, Callable] = {}
20
+ names = getattr(mod, "__all__", None)
21
+ if names:
22
+ # trust the author's export list
23
+ for n in names:
24
+ fn = getattr(mod, n, None)
25
+ if callable(fn):
26
+ out[n] = fn
27
+ return out
28
+ # otherwise, discover top-level callables defined in this module
29
+ for n in dir(mod):
30
+ if n.startswith("_"):
31
+ continue
32
+ obj = getattr(mod, n, None)
33
+ if callable(obj) and _is_defined_here(mod, obj):
34
+ out[n] = obj
35
+ return out
36
+
37
+ def _import_module_by_name(name: str) -> Optional[types.ModuleType]:
38
+ try:
39
+ return importlib.import_module(name)
40
+ except Exception:
41
+ return None
42
+
43
+ def _import_module_by_path(pkg_name: str, base_dir: str, filename: str) -> Optional[types.ModuleType]:
44
+ mod_name = f"{pkg_name}.functions"
45
+ path = os.path.join(base_dir, filename)
46
+ spec = importlib.util.spec_from_file_location(mod_name, path)
47
+ if not spec or not spec.loader:
48
+ return None
49
+ mod = importlib.util.module_from_spec(spec)
50
+ sys.modules[mod_name] = mod
51
+ spec.loader.exec_module(mod)
52
+ return mod
53
+
54
+ def _walk_functions_package(pkg_name: str, pkg_mod: types.ModuleType) -> List[types.ModuleType]:
55
+ """Import all immediate submodules in the functions/ package."""
56
+ mods: List[types.ModuleType] = [pkg_mod]
57
+ pkg_dir = os.path.dirname(pkg_mod.__file__ or "")
58
+ for info in pkgutil.iter_modules([pkg_dir]):
59
+ # only import direct children (no recursion here; easy to add if you need)
60
+ child_name = f"{pkg_mod.__name__}.{info.name}"
61
+ m = _import_module_by_name(child_name)
62
+ if m:
63
+ mods.append(m)
64
+ return mods
65
+
66
+ def _discover_functions(base_pkg: str, *, hot_reload: bool) -> List[Tuple[str, Callable, str]]:
67
+ """
68
+ Returns a list of (export_name, callable, module_basename).
69
+ Works if you have base_pkg.functions.py or base_pkg/functions/ package.
70
+ """
71
+ # Prefer normal import of '<base_pkg>.functions'
72
+ fqn = f"{base_pkg}.functions"
73
+ mod = _import_module_by_name(fqn)
74
+
75
+ if mod is None:
76
+ # fallback: sibling functions.py, even without being a package
77
+ base = _import_module_by_name(base_pkg)
78
+ if not base or not getattr(base, "__file__", None):
79
+ return []
80
+ base_dir = os.path.dirname(base.__file__)
81
+ if os.path.isfile(os.path.join(base_dir, "functions.py")):
82
+ mod = _import_module_by_path(base_pkg, base_dir, "functions.py")
83
+ else:
84
+ return []
85
+
86
+ if hot_reload:
87
+ try:
88
+ mod = importlib.reload(mod) # type: ignore[arg-type]
89
+ except Exception:
90
+ pass
91
+
92
+ results: List[Tuple[str, Callable, str]] = []
93
+ modules: List[types.ModuleType]
94
+
95
+ if hasattr(mod, "__path__"): # it's a package: import children
96
+ modules = _walk_functions_package(base_pkg, mod)
97
+ else:
98
+ modules = [mod]
99
+
100
+ for m in modules:
101
+ exported = _collect_callables(m)
102
+ module_basename = m.__name__.split(".")[-1]
103
+ for name, fn in exported.items():
104
+ results.append((name, fn, module_basename))
105
+ return results
106
+
107
+ def attach_functions(
108
+ obj_or_cls,
109
+ base_pkg: str | None = None,
110
+ hot_reload: bool = True,
111
+ prefix_with_module: bool = False,
112
+ include_private: bool = True,
113
+ only_defined_here: bool = True, # don't attach stuff imported from elsewhere
114
+ ) -> list[str]:
115
+ """
116
+ Attach all free functions found in <base_pkg>.functions (module or package)
117
+ to the *class* of obj_or_cls. Returns the list of attached attribute names.
118
+ """
119
+ cls = obj_or_cls if inspect.isclass(obj_or_cls) else obj_or_cls.__class__
120
+ # Derive "<package>.functions" from the class's module unless you pass base_pkg
121
+ caller_mod = cls.__module__
122
+ pkg_root = (base_pkg or caller_mod.rsplit(".", 1)[0]).rstrip(".")
123
+ funcs_pkg_name = f"{pkg_root}.functions"
124
+
125
+ def _import(name: str) -> ModuleType | None:
126
+ try:
127
+ if hot_reload and name in sys.modules:
128
+ return importlib.reload(sys.modules[name])
129
+ return importlib.import_module(name)
130
+ except Exception:
131
+ return None
132
+
133
+ def _is_pkg(m: ModuleType) -> bool:
134
+ return hasattr(m, "__path__")
135
+
136
+ mod = _import(funcs_pkg_name)
137
+ if mod is None:
138
+ # Nothing to attach (no functions.py or functions/ next to your class)
139
+ setattr(cls, "_attached_functions", tuple())
140
+ return []
141
+
142
+ modules: list[ModuleType] = [mod]
143
+ if _is_pkg(mod):
144
+ # attach from every submodule under functions/
145
+ for it in pkgutil.iter_modules(mod.__path__):
146
+ sub = _import(f"{funcs_pkg_name}.{it.name}")
147
+ if sub is not None:
148
+ modules.append(sub)
149
+
150
+ attached: list[str] = []
151
+ for m in modules:
152
+ for name, obj in vars(m).items():
153
+ # only callables (skip classes), and keep them sane
154
+ if not callable(obj) or isinstance(obj, type):
155
+ continue
156
+ if only_defined_here and getattr(obj, "__module__", None) != m.__name__:
157
+ continue
158
+ if not include_private and name.startswith("_"):
159
+ continue
160
+ if name.startswith("__") and name.endswith("__"):
161
+ continue
162
+ attr = f"{m.__name__.rsplit('.', 1)[-1]}__{name}" if prefix_with_module else name
163
+ try:
164
+ setattr(cls, attr, obj) # set on CLASS → becomes bound method on instances
165
+ attached.append(attr)
166
+ except Exception:
167
+ # don't explode if one name collides; keep going
168
+ continue
169
+
170
+ # handy for debugging
171
+ try:
172
+ setattr(cls, "_attached_functions", tuple(attached))
173
+ except Exception:
174
+ pass
175
+ return attached
176
+
177
+
178
+
179
+
180
+ def isTab(item):
181
+ item_lower = item.lower()
182
+ for key in ['console','tab']:
183
+ if item_lower.endswith(key):
184
+ return True
185
+ return False
186
+ def get_dir(root,item):
187
+ if None in [root]:
188
+ return None
189
+ path = root
190
+ if item != None:
191
+ path = os.path.join(path,item)
192
+ return path
193
+ def isDir(root,item=None):
194
+ path = get_dir(root,item)
195
+ if path:
196
+ return os.path.isdir(path)
197
+ def check_dir_item(root,item=None):
198
+ return (item and isTab(item) and isDir(root,item))
199
+ def get_dirs(root = None):
200
+ root = root or ABSROOT
201
+ dirpaths = [get_dir(root,item) for item in os.listdir(root) if check_dir_item(root,item)]
202
+ return dirpaths
203
+ def ifFunctionsInFile(root):
204
+ items = [os.path.join(root, "functions"),os.path.join(root, "functions.py")]
205
+ for item in items:
206
+ if os.path.exists(item):
207
+ return item
208
+
209
+
210
+ def get_for_all_tabs(root = None):
211
+ root = root or caller_path()
212
+ if os.path.isfile(root):
213
+ root = os.path.dirname(root)
214
+ all_tabs = get_files_and_dirs(root,allowed_patterns='*Tab')[0]
215
+
216
+ for ROOT in all_tabs:
217
+ if os.path.isdir(ROOT):
218
+ [apply_inits(os.path.join(ROOT,func)) for func in os.listdir(ROOT) if 'functions' == os.path.splitext(func)[0]]
219
+
220
+
221
+
222
+
223
+ def apply_inits(ROOT):
224
+ filepaths=[ROOT]
225
+ TAB_DIR = os.path.dirname(ROOT)
226
+ INIT_FUNCS_PAPTH = os.path.join(TAB_DIR, "initFuncs.py")
227
+ if_fun_dir=False
228
+ ## if os.path.isfile(ROOT):
229
+ ## dirname= os.path.dirname(ROOT)
230
+ ## basename= os.path.basename(ROOT)
231
+ ## functions_dir = os.path.join(dirname,'functions')
232
+ ## functions_path = os.path.join(functions_dir,basename)
233
+ ## input(get_clean_imports(ROOT))
234
+ ##
235
+ ## os.makedirs(functions_dir,exist_ok=True)
236
+ ## shutil.move(ROOT,functions_path)
237
+ ## ROOT=functions_dir
238
+ if os.path.isdir(ROOT):
239
+ INIT_PATH = os.path.join(ROOT, "__init__.py")
240
+
241
+ filepaths = get_files_and_dirs(directory=ROOT,allowed_exts='py',add=True)[-1]
242
+ if_fun_dir=True
243
+
244
+
245
+
246
+ # Parse top-level def names
247
+ def extract_funcs(path: str):
248
+ funcs = []
249
+ for line in read_from_file(path).splitlines():
250
+ m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
251
+ if m:
252
+ funcs.append(m.group(1))
253
+ return funcs
254
+
255
+ # Build functions/__init__.py that re-exports all discovered functions
256
+ import_lines = []
257
+ all_funcs = []
258
+ for fp in filepaths:
259
+ module = os.path.splitext(os.path.basename(fp))[0]
260
+ funcs = extract_funcs(fp)
261
+ if funcs:
262
+ import_lines.append(f"from .{module} import ({', '.join(funcs)})")
263
+ all_funcs.extend(funcs)
264
+ if if_fun_dir:
265
+ functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
266
+ write_to_file(contents=functions_init, file_path=INIT_PATH)
267
+
268
+ # Prepare the tuple literal of function names for import + loop
269
+ uniq_funcs = sorted(set(all_funcs))
270
+ func_tuple = ", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
271
+
272
+ # Generate apiConsole/initFuncs.py using the safer setattr-loop
273
+ init_funcs_src = textwrap.dedent(f"""\
274
+
275
+
276
+ from abstract_utilities import get_logFile
277
+ from .functions import ({func_tuple})
278
+ logger=get_logFile(__name__)
279
+ def initFuncs(self):
280
+ try:
281
+ for f in ({func_tuple}):
282
+ setattr(self, f.__name__, f)
283
+ except Exception as e:
284
+ logger.info(f"{{e}}")
285
+ return self
286
+ """)
287
+
288
+ write_to_file(contents=init_funcs_src, file_path=INIT_FUNCS_PAPTH)
289
+
290
+ def call_for_all_tabs():
291
+ root = get_caller_dir(2)
292
+
293
+ get_for_all_tabs(root)
@@ -0,0 +1,280 @@
1
+ # attach_functions.py — single helper you can import anywhere
2
+ # attach_dynamic.py
3
+ from __future__ import annotations
4
+ from .find_collect import *
5
+ from ..imports import *
6
+
7
+ def caller_path():
8
+ frame = inspect.stack()[1]
9
+ return os.path.abspath(frame.filename)
10
+ def _is_defined_here(mod: types.ModuleType, obj: object) -> bool:
11
+ try:
12
+ return inspect.getmodule(obj) is mod
13
+ except Exception:
14
+ return False
15
+
16
+ def _collect_callables(mod: types.ModuleType) -> Dict[str, Callable]:
17
+ out: Dict[str, Callable] = {}
18
+ names = getattr(mod, "__all__", None)
19
+ if names:
20
+ # trust the author's export list
21
+ for n in names:
22
+ fn = getattr(mod, n, None)
23
+ if callable(fn):
24
+ out[n] = fn
25
+ return out
26
+ # otherwise, discover top-level callables defined in this module
27
+ for n in dir(mod):
28
+ if n.startswith("_"):
29
+ continue
30
+ obj = getattr(mod, n, None)
31
+ if callable(obj) and _is_defined_here(mod, obj):
32
+ out[n] = obj
33
+ return out
34
+
35
+ def _import_module_by_name(name: str) -> Optional[types.ModuleType]:
36
+ try:
37
+ return importlib.import_module(name)
38
+ except Exception:
39
+ return None
40
+
41
+ def _import_module_by_path(pkg_name: str, base_dir: str, filename: str) -> Optional[types.ModuleType]:
42
+ mod_name = f"{pkg_name}.functions"
43
+ path = os.path.join(base_dir, filename)
44
+ spec = importlib.util.spec_from_file_location(mod_name, path)
45
+ if not spec or not spec.loader:
46
+ return None
47
+ mod = importlib.util.module_from_spec(spec)
48
+ sys.modules[mod_name] = mod
49
+ spec.loader.exec_module(mod)
50
+ return mod
51
+
52
+ def _walk_functions_package(pkg_name: str, pkg_mod: types.ModuleType) -> List[types.ModuleType]:
53
+ """Import all immediate submodules in the functions/ package."""
54
+ mods: List[types.ModuleType] = [pkg_mod]
55
+ pkg_dir = os.path.dirname(pkg_mod.__file__ or "")
56
+ for info in pkgutil.iter_modules([pkg_dir]):
57
+ # only import direct children (no recursion here; easy to add if you need)
58
+ child_name = f"{pkg_mod.__name__}.{info.name}"
59
+ m = _import_module_by_name(child_name)
60
+ if m:
61
+ mods.append(m)
62
+ return mods
63
+
64
+ def _discover_functions(base_pkg: str, *, hot_reload: bool) -> List[Tuple[str, Callable, str]]:
65
+ """
66
+ Returns a list of (export_name, callable, module_basename).
67
+ Works if you have base_pkg.functions.py or base_pkg/functions/ package.
68
+ """
69
+ # Prefer normal import of '<base_pkg>.functions'
70
+ fqn = f"{base_pkg}.functions"
71
+ mod = _import_module_by_name(fqn)
72
+
73
+ if mod is None:
74
+ # fallback: sibling functions.py, even without being a package
75
+ base = _import_module_by_name(base_pkg)
76
+ if not base or not getattr(base, "__file__", None):
77
+ return []
78
+ base_dir = os.path.dirname(base.__file__)
79
+ if os.path.isfile(os.path.join(base_dir, "functions.py")):
80
+ mod = _import_module_by_path(base_pkg, base_dir, "functions.py")
81
+ else:
82
+ return []
83
+
84
+ if hot_reload:
85
+ try:
86
+ mod = importlib.reload(mod) # type: ignore[arg-type]
87
+ except Exception:
88
+ pass
89
+
90
+ results: List[Tuple[str, Callable, str]] = []
91
+ modules: List[types.ModuleType]
92
+
93
+ if hasattr(mod, "__path__"): # it's a package: import children
94
+ modules = _walk_functions_package(base_pkg, mod)
95
+ else:
96
+ modules = [mod]
97
+
98
+ for m in modules:
99
+ exported = _collect_callables(m)
100
+ module_basename = m.__name__.split(".")[-1]
101
+ for name, fn in exported.items():
102
+ results.append((name, fn, module_basename))
103
+ return results
104
+
105
+ def attach_functions(
106
+ obj_or_cls,
107
+ base_pkg: str | None = None,
108
+ hot_reload: bool = True,
109
+ prefix_with_module: bool = False,
110
+ include_private: bool = True,
111
+ only_defined_here: bool = True, # don't attach stuff imported from elsewhere
112
+ ) -> list[str]:
113
+ """
114
+ Attach all free functions found in <base_pkg>.functions (module or package)
115
+ to the *class* of obj_or_cls. Returns the list of attached attribute names.
116
+ """
117
+ cls = obj_or_cls if inspect.isclass(obj_or_cls) else obj_or_cls.__class__
118
+ # Derive "<package>.functions" from the class's module unless you pass base_pkg
119
+ caller_mod = cls.__module__
120
+ pkg_root = (base_pkg or caller_mod.rsplit(".", 1)[0]).rstrip(".")
121
+ funcs_pkg_name = f"{pkg_root}.functions"
122
+
123
+ def _import(name: str) -> ModuleType | None:
124
+ try:
125
+ if hot_reload and name in sys.modules:
126
+ return importlib.reload(sys.modules[name])
127
+ return importlib.import_module(name)
128
+ except Exception:
129
+ return None
130
+
131
+ def _is_pkg(m: ModuleType) -> bool:
132
+ return hasattr(m, "__path__")
133
+
134
+ mod = _import(funcs_pkg_name)
135
+ if mod is None:
136
+ # Nothing to attach (no functions.py or functions/ next to your class)
137
+ setattr(cls, "_attached_functions", tuple())
138
+ return []
139
+
140
+ modules: list[ModuleType] = [mod]
141
+ if _is_pkg(mod):
142
+ # attach from every submodule under functions/
143
+ for it in pkgutil.iter_modules(mod.__path__):
144
+ sub = _import(f"{funcs_pkg_name}.{it.name}")
145
+ if sub is not None:
146
+ modules.append(sub)
147
+
148
+ attached: list[str] = []
149
+ for m in modules:
150
+ for name, obj in vars(m).items():
151
+ # only callables (skip classes), and keep them sane
152
+ if not callable(obj) or isinstance(obj, type):
153
+ continue
154
+ if only_defined_here and getattr(obj, "__module__", None) != m.__name__:
155
+ continue
156
+ if not include_private and name.startswith("_"):
157
+ continue
158
+ if name.startswith("__") and name.endswith("__"):
159
+ continue
160
+ attr = f"{m.__name__.rsplit('.', 1)[-1]}__{name}" if prefix_with_module else name
161
+ try:
162
+ setattr(cls, attr, obj) # set on CLASS → becomes bound method on instances
163
+ attached.append(attr)
164
+ except Exception:
165
+ # don't explode if one name collides; keep going
166
+ continue
167
+
168
+ # handy for debugging
169
+ try:
170
+ setattr(cls, "_attached_functions", tuple(attached))
171
+ except Exception:
172
+ pass
173
+ return attached
174
+
175
+
176
+
177
+
178
+ def isTab(item):
179
+ item_lower = item.lower()
180
+ for key in ['console','tab']:
181
+ if item_lower.endswith(key):
182
+ return True
183
+ return False
184
+ def get_dir(root,item):
185
+ if None in [root]:
186
+ return None
187
+ path = root
188
+ if item != None:
189
+ path = os.path.join(path,item)
190
+ return path
191
+ def isDir(root,item=None):
192
+ path = get_dir(root,item)
193
+ if path:
194
+ return os.path.isdir(path)
195
+ def check_dir_item(root,item=None):
196
+ return (item and isTab(item) and isDir(root,item))
197
+ def get_dirs(root = None):
198
+ root = root or ABSROOT
199
+ dirpaths = [get_dir(root,item) for item in os.listdir(root) if check_dir_item(root,item)]
200
+ return dirpaths
201
+ def ifFunctionsInFile(root):
202
+ items = [os.path.join(root, "functions"),os.path.join(root, "functions.py")]
203
+ for item in items:
204
+ if os.path.exists(item):
205
+ return item
206
+
207
+
208
+ def get_for_all_tabs(root = None):
209
+ root = root or get_initial_caller_dir()
210
+ if os.path.isfile(root):
211
+ root = os.path.dirname(root)
212
+ all_tabs = get_dirs(root = root)
213
+ for ROOT in all_tabs:
214
+ FUNCS_DIR = ifFunctionsInFile(ROOT)
215
+ if FUNCS_DIR == None:
216
+ for ROOT in get_dirs(root = ROOT):
217
+ apply_inits(ROOT)
218
+ else:
219
+ apply_inits(ROOT)
220
+
221
+
222
+ def apply_inits(ROOT):
223
+ FUNCS_DIR = ifFunctionsInFile(ROOT)
224
+
225
+
226
+ if_fun_dir = isDir(FUNCS_DIR)
227
+ if if_fun_dir != None:
228
+
229
+ if if_fun_dir:
230
+ filepaths = collect_filepaths(FUNCS_DIR,allowed_exts='.py',add=True)
231
+
232
+ else:
233
+ filepaths = [FUNCS_DIR]
234
+
235
+ # Parse top-level def names
236
+ def extract_funcs(path: str):
237
+ funcs = []
238
+ for line in read_from_file(path).splitlines():
239
+ m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
240
+ if m:
241
+ funcs.append(m.group(1))
242
+ return funcs
243
+
244
+ # Build functions/__init__.py that re-exports all discovered functions
245
+ import_lines = []
246
+ all_funcs = []
247
+ for fp in filepaths:
248
+ module = os.path.splitext(os.path.basename(fp))[0]
249
+ funcs = extract_funcs(fp)
250
+ if funcs:
251
+ import_lines.append(f"from .{module} import ({', '.join(funcs)})")
252
+ all_funcs.extend(funcs)
253
+ if if_fun_dir:
254
+ functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
255
+ write_to_file(contents=functions_init, file_path=os.path.join(FUNCS_DIR, "__init__.py"))
256
+
257
+ # Prepare the tuple literal of function names for import + loop
258
+ uniq_funcs = sorted(set(all_funcs))
259
+ func_tuple = ", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
260
+
261
+ # Generate apiConsole/initFuncs.py using the safer setattr-loop
262
+ init_funcs_src = textwrap.dedent(f"""\
263
+
264
+ from abstract_utilities import get_logFile
265
+ from .functions import ({func_tuple})
266
+ logger=get_logFile(__name__)
267
+ def initFuncs(self):
268
+ try:
269
+ for f in ({func_tuple}):
270
+ setattr(self, f.__name__, f)
271
+ except Exception as e:
272
+ logger.info(f"{{e}}")
273
+ return self
274
+ """)
275
+
276
+ write_to_file(contents=init_funcs_src, file_path=os.path.join(ROOT, "initFuncs.py"))
277
+
278
+ def call_for_all_tabs():
279
+ root = get_initial_caller_dir()
280
+ get_for_all_tabs(root)
@@ -0,0 +1,29 @@
1
+ from ..imports import MIME_TYPES,make_list,os
2
+ from .file_filters import get_globs
3
+ def get_file_type(file_path,types=None,default=None):
4
+ mime_types = {}
5
+ if types:
6
+ types = make_list(types)
7
+ for typ in types:
8
+ mime_types[typ] = MIME_TYPES.get(typ)
9
+ else:
10
+ mime_types = MIME_TYPES
11
+
12
+ if os.path.isfile(file_path):
13
+ basename = os.path.basename(file_path)
14
+ filename,ext = os.path.splitext(basename)
15
+ for file_type,ext_values in mime_types.items():
16
+ if ext in ext_values:
17
+ return file_type
18
+ def get_file_map(directory,types=None,default=None):
19
+ if directory and os.path.isfile(directory):
20
+ directory = os.path.dirname(directory)
21
+ all_types = {}
22
+ files = get_globs(directory)
23
+ for file in files:
24
+ file_type = get_file_type(file,types=types,default=default)
25
+ if file_type:
26
+ if file_type not in all_types:
27
+ all_types[file_type] = []
28
+ all_types[file_type].append(file)
29
+ return all_types