abstract-utilities 0.2.2.476__py3-none-any.whl → 0.2.2.540__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 (190) 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 +35 -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 +56 -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 +92 -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/imports/module_imports.py +2 -1
  35. abstract_utilities/file_utils/file_utils/type_checks.py +34 -24
  36. abstract_utilities/file_utils/imports/constants.py +84 -4
  37. abstract_utilities/file_utils/imports/imports.py +3 -18
  38. abstract_utilities/file_utils/imports/module_imports.py +2 -7
  39. abstract_utilities/file_utils/module_imports.py +12 -0
  40. abstract_utilities/file_utils/src/__init__.py +8 -0
  41. abstract_utilities/file_utils/src/file_filters.py +177 -0
  42. abstract_utilities/file_utils/src/file_reader.py +544 -0
  43. abstract_utilities/file_utils/src/file_utils.py +156 -0
  44. abstract_utilities/file_utils/src/filter_params.py +197 -0
  45. abstract_utilities/file_utils/src/find_collect.py +266 -0
  46. abstract_utilities/file_utils/src/initFunctionsGen.py +280 -0
  47. abstract_utilities/file_utils/src/map_utils.py +29 -0
  48. abstract_utilities/file_utils/src/pdf_utils.py +300 -0
  49. abstract_utilities/file_utils/src/type_checks.py +91 -0
  50. abstract_utilities/file_utils (2)/__init__.py +2 -0
  51. abstract_utilities/file_utils (2)/imports/__init__.py +2 -0
  52. abstract_utilities/file_utils (2)/imports/constants.py +118 -0
  53. abstract_utilities/file_utils (2)/imports/imports/__init__.py +3 -0
  54. abstract_utilities/file_utils (2)/imports/imports/constants.py +119 -0
  55. abstract_utilities/file_utils (2)/imports/imports/imports.py +46 -0
  56. abstract_utilities/file_utils (2)/imports/imports/module_imports.py +8 -0
  57. abstract_utilities/file_utils (2)/imports/utils/__init__.py +3 -0
  58. abstract_utilities/file_utils (2)/imports/utils/classes.py +379 -0
  59. abstract_utilities/file_utils (2)/imports/utils/clean_imps.py +155 -0
  60. abstract_utilities/file_utils (2)/imports/utils/filter_utils.py +341 -0
  61. abstract_utilities/file_utils (2)/src/__init__.py +8 -0
  62. abstract_utilities/file_utils (2)/src/file_filters.py +155 -0
  63. abstract_utilities/file_utils (2)/src/file_reader.py +604 -0
  64. abstract_utilities/file_utils (2)/src/find_collect.py +258 -0
  65. abstract_utilities/file_utils (2)/src/initFunctionsGen.py +286 -0
  66. abstract_utilities/file_utils (2)/src/map_utils.py +28 -0
  67. abstract_utilities/file_utils (2)/src/pdf_utils.py +300 -0
  68. abstract_utilities/hash_utils/__init__.py +2 -0
  69. abstract_utilities/hash_utils/hash_utils.py +5 -0
  70. abstract_utilities/hash_utils/imports/__init__.py +2 -0
  71. abstract_utilities/hash_utils/imports/imports.py +1 -0
  72. abstract_utilities/hash_utils/imports/module_imports.py +0 -0
  73. abstract_utilities/history_utils/__init__.py +2 -0
  74. abstract_utilities/history_utils/history_utils.py +37 -0
  75. abstract_utilities/history_utils/imports/__init__.py +2 -0
  76. abstract_utilities/history_utils/imports/imports.py +1 -0
  77. abstract_utilities/history_utils/imports/module_imports.py +0 -0
  78. abstract_utilities/import_utils/__init__.py +2 -0
  79. abstract_utilities/import_utils/imports/__init__.py +4 -0
  80. abstract_utilities/import_utils/imports/constants.py +2 -0
  81. abstract_utilities/import_utils/imports/imports.py +4 -0
  82. abstract_utilities/import_utils/imports/module_imports.py +6 -0
  83. abstract_utilities/import_utils/imports/utils.py +30 -0
  84. abstract_utilities/import_utils/src/__init__.py +7 -0
  85. abstract_utilities/import_utils/src/clean_imports.py +147 -0
  86. abstract_utilities/import_utils/src/dot_utils.py +69 -0
  87. abstract_utilities/import_utils/src/extract_utils.py +42 -0
  88. abstract_utilities/import_utils/src/import_functions.py +46 -0
  89. abstract_utilities/import_utils/src/import_utils.py +299 -0
  90. abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
  91. abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
  92. abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
  93. abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
  94. abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
  95. abstract_utilities/import_utils/src/package_utils.py +140 -0
  96. abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
  97. abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
  98. abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
  99. abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
  100. abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
  101. abstract_utilities/import_utils/src/pkg_utils.py +140 -0
  102. abstract_utilities/import_utils/src/sysroot_utils.py +57 -0
  103. abstract_utilities/imports.py +18 -0
  104. abstract_utilities/json_utils/__init__.py +2 -0
  105. abstract_utilities/json_utils/imports/__init__.py +2 -0
  106. abstract_utilities/json_utils/imports/imports.py +2 -0
  107. abstract_utilities/json_utils/imports/module_imports.py +5 -0
  108. abstract_utilities/json_utils/json_utils.py +743 -0
  109. abstract_utilities/list_utils/__init__.py +2 -0
  110. abstract_utilities/list_utils/imports/__init__.py +2 -0
  111. abstract_utilities/list_utils/imports/imports.py +1 -0
  112. abstract_utilities/list_utils/imports/module_imports.py +0 -0
  113. abstract_utilities/list_utils/list_utils.py +199 -0
  114. abstract_utilities/log_utils/__init__.py +5 -0
  115. abstract_utilities/log_utils/abstractLogManager.py +64 -0
  116. abstract_utilities/log_utils/call_response.py +68 -0
  117. abstract_utilities/log_utils/imports/__init__.py +2 -0
  118. abstract_utilities/log_utils/imports/imports.py +7 -0
  119. abstract_utilities/log_utils/imports/module_imports.py +2 -0
  120. abstract_utilities/log_utils/log_file.py +58 -0
  121. abstract_utilities/log_utils/logger_callable.py +49 -0
  122. abstract_utilities/math_utils/__init__.py +2 -0
  123. abstract_utilities/math_utils/imports/__init__.py +2 -0
  124. abstract_utilities/math_utils/imports/imports.py +2 -0
  125. abstract_utilities/math_utils/imports/module_imports.py +1 -0
  126. abstract_utilities/math_utils/math_utils.py +208 -0
  127. abstract_utilities/parse_utils/__init__.py +2 -0
  128. abstract_utilities/parse_utils/imports/__init__.py +3 -0
  129. abstract_utilities/parse_utils/imports/constants.py +10 -0
  130. abstract_utilities/parse_utils/imports/imports.py +2 -0
  131. abstract_utilities/parse_utils/imports/module_imports.py +4 -0
  132. abstract_utilities/parse_utils/parse_utils.py +516 -0
  133. abstract_utilities/path_utils/__init__.py +2 -0
  134. abstract_utilities/path_utils/imports/__init__.py +3 -0
  135. abstract_utilities/path_utils/imports/imports.py +1 -0
  136. abstract_utilities/path_utils/imports/module_imports.py +8 -0
  137. abstract_utilities/path_utils/path_utils.py +251 -0
  138. abstract_utilities/path_utils.py +95 -14
  139. abstract_utilities/read_write_utils/__init__.py +1 -0
  140. abstract_utilities/read_write_utils/imports/__init__.py +2 -0
  141. abstract_utilities/read_write_utils/imports/imports.py +2 -0
  142. abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
  143. abstract_utilities/read_write_utils/read_write_utils.py +338 -0
  144. abstract_utilities/read_write_utils.py +142 -20
  145. abstract_utilities/safe_utils/__init__.py +2 -0
  146. abstract_utilities/safe_utils/imports/__init__.py +3 -0
  147. abstract_utilities/safe_utils/imports/imports.py +2 -0
  148. abstract_utilities/safe_utils/imports/module_imports.py +2 -0
  149. abstract_utilities/safe_utils/safe_utils.py +136 -0
  150. abstract_utilities/ssh_utils/__init__.py +3 -1
  151. abstract_utilities/ssh_utils/classes.py +0 -1
  152. abstract_utilities/ssh_utils/cmd_utils.py +207 -0
  153. abstract_utilities/ssh_utils/imports/__init__.py +3 -0
  154. abstract_utilities/ssh_utils/imports/imports.py +5 -0
  155. abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
  156. abstract_utilities/ssh_utils/imports/utils.py +189 -0
  157. abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
  158. abstract_utilities/ssh_utils/type_checks.py +92 -0
  159. abstract_utilities/string_utils/__init__.py +4 -0
  160. abstract_utilities/string_utils/clean_utils.py +28 -0
  161. abstract_utilities/string_utils/eat_utils.py +103 -0
  162. abstract_utilities/string_utils/imports/__init__.py +3 -0
  163. abstract_utilities/string_utils/imports/imports.py +2 -0
  164. abstract_utilities/string_utils/imports/module_imports.py +2 -0
  165. abstract_utilities/string_utils/imports/utils.py +81 -0
  166. abstract_utilities/string_utils/replace_utils.py +27 -0
  167. abstract_utilities/string_utils.py +4 -1
  168. abstract_utilities/thread_utils/__init__.py +2 -0
  169. abstract_utilities/thread_utils/imports/__init__.py +2 -0
  170. abstract_utilities/thread_utils/imports/imports.py +2 -0
  171. abstract_utilities/thread_utils/imports/module_imports.py +2 -0
  172. abstract_utilities/thread_utils/thread_utils.py +140 -0
  173. abstract_utilities/time_utils/__init__.py +2 -0
  174. abstract_utilities/time_utils/imports/__init__.py +2 -0
  175. abstract_utilities/time_utils/imports/imports.py +3 -0
  176. abstract_utilities/time_utils/imports/module_imports.py +1 -0
  177. abstract_utilities/time_utils/time_utils.py +392 -0
  178. abstract_utilities/type_utils/__init__.py +3 -0
  179. abstract_utilities/type_utils/alpha_utils.py +59 -0
  180. abstract_utilities/type_utils/imports/__init__.py +2 -0
  181. abstract_utilities/type_utils/imports/imports.py +4 -0
  182. abstract_utilities/type_utils/imports/module_imports.py +1 -0
  183. abstract_utilities/type_utils/num_utils.py +19 -0
  184. abstract_utilities/type_utils/type_utils.py +981 -0
  185. {abstract_utilities-0.2.2.476.dist-info → abstract_utilities-0.2.2.540.dist-info}/METADATA +1 -1
  186. abstract_utilities-0.2.2.540.dist-info/RECORD +263 -0
  187. imports/__init__.py +36 -0
  188. abstract_utilities-0.2.2.476.dist-info/RECORD +0 -92
  189. {abstract_utilities-0.2.2.476.dist-info → abstract_utilities-0.2.2.540.dist-info}/WHEEL +0 -0
  190. {abstract_utilities-0.2.2.476.dist-info → abstract_utilities-0.2.2.540.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ # safe_import_utils.py
2
+ from ..imports import *
3
+ from .import_functions import *
4
+
5
+ def ensure_import_pkg_js(import_pkg_js,file_path=None):
6
+ import_pkg_js = import_pkg_js or {"context":{}}
7
+ if "context" not in import_pkg_js:
8
+ import_pkg_js["context"]={}
9
+ for key in ["nulines","file_path","all_data"]:
10
+ if key not in import_pkg_js["context"]:
11
+ import_pkg_js["context"][key]=[]
12
+ if file_path and file_path != import_pkg_js["context"]["file_path"]:
13
+ found=False
14
+ nu_data = {"file_path":import_pkg_js["context"]["file_path"],"nulines":import_pkg_js["context"]["nulines"]}
15
+ for i,data in enumerate(import_pkg_js["context"]["all_data"]):
16
+ if data.get('file_path') == import_pkg_js["context"]["file_path"]:
17
+ import_pkg_js["context"]["all_data"][i] = nu_data
18
+ found = True
19
+ break
20
+ if found == False:
21
+ import_pkg_js["context"]["all_data"].append(nu_data)
22
+ import_pkg_js["context"]["nulines"]=[]
23
+ import_pkg_js["context"]["file_path"]=file_path
24
+ return import_pkg_js
25
+ def ensure_package_context(file: str):
26
+ """
27
+ Ensure that running this file directly still gives it the correct package.
28
+ Sets sys.path and __package__ based on the __init__.py chain.
29
+ """
30
+ file = file or os.getcwd()
31
+ here = Path(file).resolve()
32
+ top_pkg_dir = find_top_pkg_dir(here)
33
+ if not top_pkg_dir:
34
+ raise RuntimeError(f"No package context above {here}. Add __init__.py files up the tree.")
35
+
36
+ sysroot = top_pkg_dir.parent # dir ABOVE the top package (e.g., .../src)
37
+ if str(sysroot) not in sys.path:
38
+ sys.path.insert(0, str(sysroot))
39
+
40
+ # Compute this module's package (exclude the filename)
41
+ parts = here.with_suffix("").relative_to(sysroot).parts
42
+ pkg_name = ".".join(parts[:-1]) # e.g. abstract_ide.consoles.launcherWindowTab
43
+
44
+ # When run as a script, __package__ is empty -> set it
45
+ if (__name__ == "__main__") and (not globals().get("__package__")):
46
+ globals()["__package__"] = pkg_name
47
+ # --- tiny utils ---
48
+ def find_top_package_dir(p: Path) -> Path | None:
49
+ p = p.resolve()
50
+ if p.is_file():
51
+ p = p.parent
52
+ top = None
53
+ while (p / "__init__.py").exists():
54
+ top = p
55
+ if p.parent == p:
56
+ break
57
+ p = p.parent
58
+ return top
59
+
60
+ def derive_package_for_file(file: str) -> tuple[str, Path]:
61
+ """Return (pkg_name, sysroot) for the module file."""
62
+ here = Path(file).resolve()
63
+ top_pkg_dir = find_top_pkg_dir(here)
64
+ if not top_pkg_dir:
65
+ raise RuntimeError(f"No package context above {here}. Add __init__.py up the tree.")
66
+ sysroot = top_pkg_dir.parent
67
+ if str(sysroot) not in sys.path:
68
+ sys.path.insert(0, str(sysroot))
69
+ parts = here.with_suffix("").relative_to(sysroot).parts
70
+ pkg_name = ".".join(parts[:-1]) # package of the module (drop the filename)
71
+ return pkg_name, sysroot
72
+
73
+ def ensure_caller_package(caller_file: str, caller_globals: dict | None = None) -> str:
74
+ """
75
+ Ensure sysroot is on sys.path and return the caller's dotted package.
76
+ Optionally set caller_globals['__package__'] when running as a script.
77
+ """
78
+ pkg, _ = derive_package_for_file(caller_file)
79
+ if caller_globals and caller_globals.get("__name__") == "__main__" and not caller_globals.get("__package__"):
80
+ caller_globals["__package__"] = pkg
81
+ return pkg
82
+ def get_import_pkg(line):
83
+ if is_line_group_import(line):
84
+ return clean_line(line.split(FROM_TAG)[1].split(IMPORT_TAG)[0])
85
+ def get_imports_from_import_pkg(line):
86
+ if is_line_group_import(line):
87
+ return get_cleaned_import_list(line,commaClean=True)
88
+ def add_imports_to_import_pkg_js(import_pkg,imports,import_pkg_js=None):
89
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
90
+ imports = clean_imports(imports)
91
+ if import_pkg not in import_pkg_js:
92
+ i = len(import_pkg_js["context"]["nulines"])
93
+ import_pkg_js[import_pkg]={"imports":imports,"line":i}
94
+ import_line = f"from {import_pkg} import "
95
+ if import_pkg == "import":
96
+ import_line = IMPORT_TAG
97
+ import_pkg_js["context"]["nulines"].append(import_line)
98
+ else:
99
+ import_pkg_js[import_pkg]["imports"]+=imports
100
+ return import_pkg_js
101
+ def update_import_pkg_js(line,import_pkg_js=None):
102
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
103
+ if is_line_group_import(line):
104
+ import_pkg = get_import_pkg(line)
105
+ imports = get_imports_from_import_pkg(line)
106
+ import_pkg_js = add_imports_to_import_pkg_js(import_pkg,imports,import_pkg_js=import_pkg_js)
107
+ else:
108
+ if len(import_pkg_js["context"]["nulines"]) >0 and line == '' and is_line_import(import_pkg_js["context"]["nulines"][-1]):
109
+ pass
110
+ else:
111
+ import_pkg_js["context"]["nulines"].append(line)
112
+ return import_pkg_js
113
+ # --- public API ---
114
+ def safe_import(
115
+ name: str,
116
+ *,
117
+ member: str | None = None,
118
+ package: str | None = None,
119
+ file: str | None = None,
120
+ caller_globals: dict | None = None,
121
+ ):
122
+ """
123
+ Import `name` (relative or absolute).
124
+ - If `name` is relative and `package` is missing, derive it from `file` (or caller).
125
+ - If running the caller as a script, we can set its __package__ when caller_globals is provided.
126
+ """
127
+ if file is None:
128
+ # best-effort: use the immediate caller's file
129
+ frame = inspect.currentframe()
130
+ assert frame is not None
131
+ outer = frame.f_back
132
+ caller_file = (outer.f_globals.get("__file__") if outer else None) or __file__
133
+ else:
134
+ caller_file = file
135
+
136
+ if name.startswith(".") and not package:
137
+ package = ensure_caller_package(caller_file, caller_globals=caller_globals)
138
+
139
+ mod = importlib.import_module(name, package=package)
140
+ return getattr(mod, member) if member else mod
@@ -0,0 +1,139 @@
1
+ import ast
2
+ from pathlib import Path
3
+ from importlib.util import find_spec
4
+ from typing import Dict, Set
5
+ import ast, sys
6
+ from pathlib import Path
7
+ from importlib.util import find_spec
8
+ from typing import Dict, Set
9
+ from src.abstract_utilities.import_utils import *
10
+ STDLIB_NAMES = set(sys.builtin_module_names)
11
+ def parse_imports(file_path: Path):
12
+ """Return list of (module, level) for every import/from-import."""
13
+ try:
14
+ src = file_path.read_text(errors="ignore")
15
+ tree = ast.parse(src, filename=str(file_path))
16
+ except Exception:
17
+ return []
18
+ imports = []
19
+ for node in ast.walk(tree):
20
+ if isinstance(node, ast.Import):
21
+ for alias in node.names:
22
+ imports.append((alias.name, 0))
23
+ elif isinstance(node, ast.ImportFrom):
24
+ imports.append((node.module, node.level))
25
+ return imports
26
+
27
+
28
+ def resolve_relative_import(base_file: Path, module: str | None, level: int) -> Path | None:
29
+ """Follow a relative import path to its real file if it exists."""
30
+ base = base_file.parent
31
+ for _ in range(level - 1):
32
+ base = base.parent
33
+ if not module:
34
+ target = base
35
+ else:
36
+ target = base / module.replace(".", "/")
37
+ if (target / "__init__.py").exists():
38
+ return target / "__init__.py"
39
+ if target.with_suffix(".py").exists():
40
+ return target.with_suffix(".py")
41
+ return None
42
+
43
+
44
+
45
+
46
+ def classify_import(mod_name: str, root_pkg: str) -> str:
47
+ """Return 'local', 'internal', or 'external'."""
48
+ if not mod_name:
49
+ return "unknown"
50
+ if mod_name.startswith("."):
51
+ return "local"
52
+ if mod_name.split(".")[0] == root_pkg:
53
+ return "internal"
54
+ if mod_name.split(".")[0] in STDLIB_NAMES:
55
+ return "stdlib"
56
+ return "external"
57
+
58
+
59
+ def follow_imports(entry: Path, root_pkg: str,
60
+ visited: Dict[Path, Dict[str, Set[str]]] | None = None):
61
+ """
62
+ Recursively follow only internal/local imports.
63
+ Returns {file_path: {'internal': set(), 'external': set()}}
64
+ """
65
+ visited = visited or {}
66
+ if entry in visited:
67
+ return visited
68
+
69
+ visited[entry] = {"internal": set(), "external": set()}
70
+
71
+ for mod, level in parse_imports(entry):
72
+ if level > 0:
73
+ target = resolve_relative_import(entry, mod, level)
74
+ if target:
75
+ visited[entry]["internal"].add(str(target))
76
+ follow_imports(target, root_pkg, visited)
77
+ continue
78
+
79
+ category = classify_import(mod, root_pkg)
80
+ if category == "internal":
81
+ spec = find_spec(mod)
82
+ if spec and spec.origin and spec.origin.endswith(".py"):
83
+ visited[entry]["internal"].add(spec.origin)
84
+ follow_imports(Path(spec.origin), root_pkg, visited)
85
+ elif category == "external":
86
+ visited[entry]["external"].add(mod)
87
+ elif category == "stdlib":
88
+ # stdlib gets treated like external but labeled
89
+ visited[entry]["external"].add(mod + " # stdlib")
90
+
91
+ return visited
92
+
93
+
94
+
95
+ def build_master_imports(entry: Path, root_pkg: str, output: Path):
96
+ trace = follow_imports(entry, root_pkg)
97
+ lines = ["# Auto-generated master imports for abstract_utilities\n"]
98
+ all_modules = set()
99
+ external_modules = set()
100
+ imports = get_all_imports(path)
101
+ for _, data in trace.items():
102
+ for dep in data["internal"]:
103
+ path = Path(dep)
104
+ if path.suffix != ".py":
105
+ continue
106
+ try:
107
+ rel_parts = path.with_suffix("").parts
108
+ idx = rel_parts.index(root_pkg)
109
+ dotted = ".".join(rel_parts[idx:])
110
+ all_modules.add(dotted)
111
+ except ValueError:
112
+ continue
113
+ external_modules.update(data["external"])
114
+
115
+ for mod in sorted(all_modules):
116
+ short = mod.split(".", 1)[-1]
117
+ lines.append(f"from .{short} import *")
118
+
119
+ if external_modules:
120
+ lines.append("\n# External / stdlib imports (not traced, for reference)")
121
+ for ext in sorted(external_modules):
122
+ lines.append(f"# {ext}")
123
+
124
+ output.parent.mkdir(parents=True, exist_ok=True)
125
+ output.write_text("\n".join(lines+str(imports)))
126
+ print(f"✅ wrote master imports hub → {output}")
127
+
128
+
129
+
130
+ if __name__ == "__main__":
131
+ entry = Path(
132
+ "/home/flerb/Documents/pythonTools/modules/src/modules/abstract_utilities/src/"
133
+ "abstract_utilities/import_utils/src/import_functions.py"
134
+ )
135
+
136
+
137
+ pkg = "abstract_utilities"
138
+ out = entry.parents[4] / "imports" / "__init__.py"
139
+ build_master_imports(entry, pkg, out)
@@ -0,0 +1,27 @@
1
+ import sys, os
2
+ from pathlib import Path
3
+ from .path_utils import find_top_package_dir, derive_package_for_file
4
+
5
+ def ensure_package_context(file: str):
6
+ """Ensure that running this file directly gives correct package context."""
7
+ file = file or os.getcwd()
8
+ here = Path(file).resolve()
9
+ top_pkg_dir = find_top_package_dir(here)
10
+ if not top_pkg_dir:
11
+ raise RuntimeError(f"No package context above {here}. Add __init__.py files up the tree.")
12
+
13
+ sysroot = top_pkg_dir.parent
14
+ if str(sysroot) not in sys.path:
15
+ sys.path.insert(0, str(sysroot))
16
+
17
+ parts = here.with_suffix("").relative_to(sysroot).parts
18
+ pkg_name = ".".join(parts[:-1])
19
+ if (__name__ == "__main__") and not globals().get("__package__"):
20
+ globals()["__package__"] = pkg_name
21
+
22
+ def ensure_caller_package(caller_file: str, caller_globals: dict | None = None) -> str:
23
+ """Ensure sysroot is on sys.path and return caller's dotted package name."""
24
+ pkg, _ = derive_package_for_file(caller_file)
25
+ if caller_globals and caller_globals.get("__name__") == "__main__" and not caller_globals.get("__package__"):
26
+ caller_globals["__package__"] = pkg
27
+ return pkg
@@ -0,0 +1,53 @@
1
+ from ..imports import *
2
+ from .import_functions import *
3
+
4
+ def ensure_import_pkg_js(import_pkg_js=None, file_path=None):
5
+ import_pkg_js = import_pkg_js or {"context": {}}
6
+ if "context" not in import_pkg_js:
7
+ import_pkg_js["context"] = {}
8
+ for key in ["nulines", "file_path", "all_data"]:
9
+ import_pkg_js["context"].setdefault(key, [] if key != "file_path" else None)
10
+
11
+ if file_path and file_path != import_pkg_js["context"].get("file_path"):
12
+ found = False
13
+ nu_data = {
14
+ "file_path": import_pkg_js["context"]["file_path"],
15
+ "nulines": import_pkg_js["context"]["nulines"]
16
+ }
17
+ for i, data in enumerate(import_pkg_js["context"]["all_data"]):
18
+ if data.get("file_path") == import_pkg_js["context"]["file_path"]:
19
+ import_pkg_js["context"]["all_data"][i] = nu_data
20
+ found = True
21
+ break
22
+ if not found:
23
+ import_pkg_js["context"]["all_data"].append(nu_data)
24
+ import_pkg_js["context"]["nulines"] = []
25
+ import_pkg_js["context"]["file_path"] = file_path
26
+ return import_pkg_js
27
+
28
+ def add_imports_to_import_pkg_js(import_pkg, imports, import_pkg_js=None):
29
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
30
+ imports = clean_imports(imports)
31
+ if import_pkg not in import_pkg_js:
32
+ i = len(import_pkg_js["context"]["nulines"])
33
+ file_path = import_pkg_js["context"]["file_path"]
34
+ file_parts = get_file_parts(file_path)
35
+ dirname = file_parts["dirname"]
36
+ import_pkg_js[import_pkg] = {"imports": imports, "line": i}
37
+ import_pkg_js["context"]["nulines"].append(f"from {import_pkg} import ")
38
+ else:
39
+ import_pkg_js[import_pkg]["imports"] += imports
40
+ return import_pkg_js
41
+
42
+ def update_import_pkg_js(line, import_pkg_js=None):
43
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
44
+ if is_line_group_import(line):
45
+ import_pkg = get_import_pkg(line)
46
+ imports = get_imports_from_import_pkg(line)
47
+ import_pkg_js = add_imports_to_import_pkg_js(import_pkg, imports, import_pkg_js=import_pkg_js)
48
+ else:
49
+ if import_pkg_js["context"]["nulines"] and line == "" and is_line_import(import_pkg_js["context"]["nulines"][-1]):
50
+ pass
51
+ else:
52
+ import_pkg_js["context"]["nulines"].append(line)
53
+ return import_pkg_js
@@ -0,0 +1,28 @@
1
+ import os, sys
2
+ from pathlib import Path
3
+
4
+ def find_top_package_dir(p: Path) -> Path | None:
5
+ """Walk upward until the topmost __init__.py-containing directory."""
6
+ p = p.resolve()
7
+ if p.is_file():
8
+ p = p.parent
9
+ top = None
10
+ while (p / "__init__.py").exists():
11
+ top = p
12
+ if p.parent == p:
13
+ break
14
+ p = p.parent
15
+ return top
16
+
17
+ def derive_package_for_file(file: str) -> tuple[str, Path]:
18
+ """Return (pkg_name, sysroot) for the module file."""
19
+ here = Path(file).resolve()
20
+ top_pkg_dir = find_top_package_dir(here)
21
+ if not top_pkg_dir:
22
+ raise RuntimeError(f"No package context above {here}. Add __init__.py up the tree.")
23
+ sysroot = top_pkg_dir.parent
24
+ if str(sysroot) not in sys.path:
25
+ sys.path.insert(0, str(sysroot))
26
+ parts = here.with_suffix("").relative_to(sysroot).parts
27
+ pkg_name = ".".join(parts[:-1])
28
+ return pkg_name, sysroot
@@ -0,0 +1,27 @@
1
+ import inspect, importlib
2
+ from .context_utils import ensure_caller_package
3
+
4
+ def safe_import(
5
+ name: str,
6
+ *,
7
+ member: str | None = None,
8
+ package: str | None = None,
9
+ file: str | None = None,
10
+ caller_globals: dict | None = None,
11
+ ):
12
+ """
13
+ Safe dynamic import that resolves relative imports when run as a script.
14
+ """
15
+ if file is None:
16
+ frame = inspect.currentframe()
17
+ assert frame is not None
18
+ outer = frame.f_back
19
+ caller_file = (outer.f_globals.get("__file__") if outer else None) or __file__
20
+ else:
21
+ caller_file = file
22
+
23
+ if name.startswith(".") and not package:
24
+ package = ensure_caller_package(caller_file, caller_globals=caller_globals)
25
+
26
+ mod = importlib.import_module(name, package=package)
27
+ return getattr(mod, member) if member else mod
@@ -0,0 +1,140 @@
1
+ # safe_import_utils.py
2
+ from ..imports import *
3
+ from .import_functions import *
4
+
5
+ def ensure_import_pkg_js(import_pkg_js,file_path=None):
6
+ import_pkg_js = import_pkg_js or {"context":{}}
7
+ if "context" not in import_pkg_js:
8
+ import_pkg_js["context"]={}
9
+ for key in ["nulines","file_path","all_data"]:
10
+ if key not in import_pkg_js["context"]:
11
+ import_pkg_js["context"][key]=[]
12
+ if file_path and file_path != import_pkg_js["context"]["file_path"]:
13
+ found=False
14
+ nu_data = {"file_path":import_pkg_js["context"]["file_path"],"nulines":import_pkg_js["context"]["nulines"]}
15
+ for i,data in enumerate(import_pkg_js["context"]["all_data"]):
16
+ if data.get('file_path') == import_pkg_js["context"]["file_path"]:
17
+ import_pkg_js["context"]["all_data"][i] = nu_data
18
+ found = True
19
+ break
20
+ if found == False:
21
+ import_pkg_js["context"]["all_data"].append(nu_data)
22
+ import_pkg_js["context"]["nulines"]=[]
23
+ import_pkg_js["context"]["file_path"]=file_path
24
+ return import_pkg_js
25
+ def ensure_package_context(file: str):
26
+ """
27
+ Ensure that running this file directly still gives it the correct package.
28
+ Sets sys.path and __package__ based on the __init__.py chain.
29
+ """
30
+ file = file or os.getcwd()
31
+ here = Path(file).resolve()
32
+ top_pkg_dir = find_top_pkg_dir(here)
33
+ if not top_pkg_dir:
34
+ raise RuntimeError(f"No package context above {here}. Add __init__.py files up the tree.")
35
+
36
+ sysroot = top_pkg_dir.parent # dir ABOVE the top package (e.g., .../src)
37
+ if str(sysroot) not in sys.path:
38
+ sys.path.insert(0, str(sysroot))
39
+
40
+ # Compute this module's package (exclude the filename)
41
+ parts = here.with_suffix("").relative_to(sysroot).parts
42
+ pkg_name = ".".join(parts[:-1]) # e.g. abstract_ide.consoles.launcherWindowTab
43
+
44
+ # When run as a script, __package__ is empty -> set it
45
+ if (__name__ == "__main__") and (not globals().get("__package__")):
46
+ globals()["__package__"] = pkg_name
47
+ # --- tiny utils ---
48
+ def find_top_package_dir(p: Path) -> Path | None:
49
+ p = p.resolve()
50
+ if p.is_file():
51
+ p = p.parent
52
+ top = None
53
+ while (p / "__init__.py").exists():
54
+ top = p
55
+ if p.parent == p:
56
+ break
57
+ p = p.parent
58
+ return top
59
+
60
+ def derive_package_for_file(file: str) -> tuple[str, Path]:
61
+ """Return (pkg_name, sysroot) for the module file."""
62
+ here = Path(file).resolve()
63
+ top_pkg_dir = find_top_pkg_dir(here)
64
+ if not top_pkg_dir:
65
+ raise RuntimeError(f"No package context above {here}. Add __init__.py up the tree.")
66
+ sysroot = top_pkg_dir.parent
67
+ if str(sysroot) not in sys.path:
68
+ sys.path.insert(0, str(sysroot))
69
+ parts = here.with_suffix("").relative_to(sysroot).parts
70
+ pkg_name = ".".join(parts[:-1]) # package of the module (drop the filename)
71
+ return pkg_name, sysroot
72
+
73
+ def ensure_caller_package(caller_file: str, caller_globals: dict | None = None) -> str:
74
+ """
75
+ Ensure sysroot is on sys.path and return the caller's dotted package.
76
+ Optionally set caller_globals['__package__'] when running as a script.
77
+ """
78
+ pkg, _ = derive_package_for_file(caller_file)
79
+ if caller_globals and caller_globals.get("__name__") == "__main__" and not caller_globals.get("__package__"):
80
+ caller_globals["__package__"] = pkg
81
+ return pkg
82
+ def get_import_pkg(line):
83
+ if is_line_group_import(line):
84
+ return clean_line(line.split(FROM_TAG)[1].split(IMPORT_TAG)[0])
85
+ def get_imports_from_import_pkg(line):
86
+ if is_line_group_import(line):
87
+ return get_cleaned_import_list(line,commaClean=True)
88
+ def add_imports_to_import_pkg_js(import_pkg,imports,import_pkg_js=None):
89
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
90
+ imports = clean_imports(imports)
91
+ if import_pkg not in import_pkg_js:
92
+ i = len(import_pkg_js["context"]["nulines"])
93
+ import_pkg_js[import_pkg]={"imports":imports,"line":i}
94
+ import_line = f"from {import_pkg} import "
95
+ if import_pkg == "import":
96
+ import_line = IMPORT_TAG
97
+ import_pkg_js["context"]["nulines"].append(import_line)
98
+ else:
99
+ import_pkg_js[import_pkg]["imports"]+=imports
100
+ return import_pkg_js
101
+ def update_import_pkg_js(line,import_pkg_js=None):
102
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js)
103
+ if is_line_group_import(line):
104
+ import_pkg = get_import_pkg(line)
105
+ imports = get_imports_from_import_pkg(line)
106
+ import_pkg_js = add_imports_to_import_pkg_js(import_pkg,imports,import_pkg_js=import_pkg_js)
107
+ else:
108
+ if len(import_pkg_js["context"]["nulines"]) >0 and line == '' and is_line_import(import_pkg_js["context"]["nulines"][-1]):
109
+ pass
110
+ else:
111
+ import_pkg_js["context"]["nulines"].append(line)
112
+ return import_pkg_js
113
+ # --- public API ---
114
+ def safe_import(
115
+ name: str,
116
+ *,
117
+ member: str | None = None,
118
+ package: str | None = None,
119
+ file: str | None = None,
120
+ caller_globals: dict | None = None,
121
+ ):
122
+ """
123
+ Import `name` (relative or absolute).
124
+ - If `name` is relative and `package` is missing, derive it from `file` (or caller).
125
+ - If running the caller as a script, we can set its __package__ when caller_globals is provided.
126
+ """
127
+ if file is None:
128
+ # best-effort: use the immediate caller's file
129
+ frame = inspect.currentframe()
130
+ assert frame is not None
131
+ outer = frame.f_back
132
+ caller_file = (outer.f_globals.get("__file__") if outer else None) or __file__
133
+ else:
134
+ caller_file = file
135
+
136
+ if name.startswith(".") and not package:
137
+ package = ensure_caller_package(caller_file, caller_globals=caller_globals)
138
+
139
+ mod = importlib.import_module(name, package=package)
140
+ return getattr(mod, member) if member else mod
@@ -0,0 +1,57 @@
1
+ from ..imports import *
2
+ from .dot_utils import *
3
+
4
+ def ensure_on_path(p: Path):
5
+ s = str(p)
6
+ if s not in sys.path:
7
+ sys.path.insert(0, s)
8
+ def get_sysroot(filepath,i):
9
+ for j in range(i):
10
+ filepath = os.path.dirname(filepath)
11
+ return filepath
12
+
13
+ def get_dot_range_sysroot(filepath):
14
+ sysroot = filepath
15
+ while True:
16
+ dot_range = get_dot_range(is_import_or_init(sysroot))
17
+ if dot_range == 0:
18
+ break
19
+ sysroot = get_sysroot(sysroot,dot_range)
20
+
21
+ return sysroot
22
+
23
+ def is_import_or_init(sysroot,likely=None):
24
+ file_data = get_file_parts(sysroot)
25
+ nuroot = sysroot
26
+ dirname = file_data.get('dirname')
27
+ if os.path.isdir(sysroot):
28
+ dirname = sysroot
29
+ ext = file_data.get('ext')
30
+ filename = file_data.get('filename')
31
+
32
+ candidates = []
33
+ if likely:
34
+ candidates += [os.path.join(dirname,f"{likely}.py"),os.path.join(dirname,likely)]
35
+ candidates += [os.path.join(dirname,f"{filename}.py"),os.path.join(dirname,filename)]
36
+ files: List[Path] = []
37
+ for item in candidates:
38
+
39
+ if os.path.exists(item):
40
+ if os.path.isdir(item):
41
+
42
+ nuroot=None
43
+ init_name = '__init__.py'
44
+ rootList = os.listdir(item)
45
+ for basename in rootList:
46
+ if get_file_parts(basename,'filename') == filename:
47
+ nuroot = os.path.join(item,basename)
48
+ break
49
+ if init_name in rootList:
50
+ nuroot = os.path.join(item,init_name)
51
+ break
52
+
53
+ else:
54
+ nuroot=sysroot
55
+ break
56
+
57
+ return nuroot
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+ import re,pexpect,shlex,ezodf,tiktoken,geopandas as gpd,os,PyPDF2,json,tempfile,requests
3
+ import textwrap,pdfplumber,math,hashlib,pandas as pd,platform,textwrap as tw,glob,asyncio
4
+ import fnmatch,importlib,shutil,sys,time,threading,posixpath,importlib.util,types
5
+ import subprocess,pytesseract,queue,logging,functools,pathlib,pkgutil,inspect
6
+ from typing import *
7
+ from datetime import timedelta,datetime
8
+ from flask import jsonify
9
+ from logging.handlers import RotatingFileHandler
10
+ from pathlib import Path
11
+ from functools import reduce
12
+ from types import MethodType,ModuleType
13
+ from werkzeug.utils import secure_filename
14
+ from werkzeug.datastructures import FileStorage
15
+ from pdf2image import convert_from_path # only used for OCR fallback
16
+ from dataclasses import dataclass,field,asdict
17
+ from pprint import pprint
18
+ from dotenv import load_dotenv
@@ -0,0 +1,2 @@
1
+ from .imports import *
2
+ from .json_utils import *
@@ -0,0 +1,2 @@
1
+ from .imports import *
2
+ from .module_imports import *
@@ -0,0 +1,2 @@
1
+ from ...imports import json,re,os,logging
2
+ from typing import *
@@ -0,0 +1,5 @@
1
+ from ...read_write_utils import check_read_write_params, read_from_file, write_to_file
2
+ from ...compare_utils import get_closest_match_from_list
3
+ from ...directory_utils import makedirs
4
+ from ...list_utils import make_list
5
+ from ...class_utils import alias