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.
- abstract_utilities/__init__.py +24 -16
- abstract_utilities/circular_import_finder.py +222 -0
- abstract_utilities/circular_import_finder2.py +118 -0
- abstract_utilities/class_utils/__init__.py +7 -0
- abstract_utilities/class_utils/abstract_classes.py +144 -0
- abstract_utilities/class_utils/caller_utils.py +92 -0
- abstract_utilities/class_utils/class_utils.py +109 -0
- abstract_utilities/class_utils/function_utils.py +153 -0
- abstract_utilities/class_utils/global_utils.py +71 -0
- abstract_utilities/class_utils/imports/__init__.py +2 -0
- abstract_utilities/class_utils/imports/imports.py +2 -0
- abstract_utilities/class_utils/imports/utils.py +40 -0
- abstract_utilities/class_utils/module_utils.py +63 -0
- abstract_utilities/directory_utils/__init__.py +2 -0
- abstract_utilities/directory_utils/directory_utils.py +94 -0
- abstract_utilities/directory_utils/imports/__init__.py +2 -0
- abstract_utilities/directory_utils/imports/imports.py +1 -0
- abstract_utilities/directory_utils/imports/module_imports.py +2 -0
- abstract_utilities/directory_utils/name_utils.py +43 -0
- abstract_utilities/directory_utils/size_utils.py +57 -0
- abstract_utilities/directory_utils/src/__init__.py +4 -0
- abstract_utilities/directory_utils/src/directory_utils.py +110 -0
- abstract_utilities/directory_utils/src/name_utils.py +43 -0
- abstract_utilities/directory_utils/src/size_utils.py +57 -0
- abstract_utilities/directory_utils/src/utils.py +116 -0
- abstract_utilities/directory_utils/utils.py +116 -0
- abstract_utilities/env_utils/imports/imports.py +5 -3
- abstract_utilities/error_utils/__init__.py +2 -0
- abstract_utilities/error_utils/error_utils.py +25 -0
- abstract_utilities/error_utils/imports/__init__.py +2 -0
- abstract_utilities/error_utils/imports/imports.py +1 -0
- abstract_utilities/error_utils/imports/module_imports.py +1 -0
- abstract_utilities/file_utils/__init__.py +1 -2
- abstract_utilities/file_utils/file_utils/type_checks.py +2 -1
- abstract_utilities/file_utils/imports/classes.py +59 -55
- abstract_utilities/file_utils/imports/constants.py +84 -4
- abstract_utilities/file_utils/imports/imports.py +2 -21
- abstract_utilities/file_utils/imports/module_imports.py +3 -8
- abstract_utilities/file_utils/module_imports.py +12 -0
- abstract_utilities/file_utils/src/__init__.py +7 -0
- abstract_utilities/file_utils/src/file_filters/__init__.py +1 -0
- abstract_utilities/file_utils/src/file_filters/ensure_utils.py +490 -0
- abstract_utilities/file_utils/src/file_filters/filter_params.py +150 -0
- abstract_utilities/file_utils/src/file_filters/filter_utils.py +78 -0
- abstract_utilities/file_utils/src/file_filters/predicate_utils.py +44 -0
- abstract_utilities/file_utils/src/file_filters.py +177 -0
- abstract_utilities/file_utils/src/file_reader.py +543 -0
- abstract_utilities/file_utils/src/file_utils.py +156 -0
- abstract_utilities/file_utils/src/filter_params.py +197 -0
- abstract_utilities/file_utils/src/find_collect.py +200 -0
- abstract_utilities/file_utils/src/find_content.py +210 -0
- abstract_utilities/file_utils/src/initFunctionsGen.py +293 -0
- abstract_utilities/file_utils/src/initFunctionsGens.py +280 -0
- abstract_utilities/file_utils/src/map_utils.py +29 -0
- abstract_utilities/file_utils/src/pdf_utils.py +300 -0
- abstract_utilities/file_utils/src/reader_utils/__init__.py +4 -0
- abstract_utilities/file_utils/src/reader_utils/directory_reader.py +53 -0
- abstract_utilities/file_utils/src/reader_utils/file_reader.py +543 -0
- abstract_utilities/file_utils/src/reader_utils/file_readers.py +376 -0
- abstract_utilities/file_utils/src/reader_utils/imports.py +18 -0
- abstract_utilities/file_utils/src/reader_utils/pdf_utils.py +300 -0
- abstract_utilities/file_utils/src/type_checks.py +91 -0
- abstract_utilities/file_utils (2)/__init__.py +2 -0
- abstract_utilities/file_utils (2)/imports/__init__.py +2 -0
- abstract_utilities/file_utils (2)/imports/constants.py +118 -0
- abstract_utilities/file_utils (2)/imports/imports/__init__.py +3 -0
- abstract_utilities/file_utils (2)/imports/imports/constants.py +119 -0
- abstract_utilities/file_utils (2)/imports/imports/imports.py +46 -0
- abstract_utilities/file_utils (2)/imports/imports/module_imports.py +8 -0
- abstract_utilities/file_utils (2)/imports/utils/__init__.py +3 -0
- abstract_utilities/file_utils (2)/imports/utils/classes.py +379 -0
- abstract_utilities/file_utils (2)/imports/utils/clean_imps.py +155 -0
- abstract_utilities/file_utils (2)/imports/utils/filter_utils.py +341 -0
- abstract_utilities/file_utils (2)/src/__init__.py +8 -0
- abstract_utilities/file_utils (2)/src/file_filters.py +155 -0
- abstract_utilities/file_utils (2)/src/file_reader.py +604 -0
- abstract_utilities/file_utils (2)/src/find_collect.py +258 -0
- abstract_utilities/file_utils (2)/src/initFunctionsGen.py +286 -0
- abstract_utilities/file_utils (2)/src/map_utils.py +28 -0
- abstract_utilities/file_utils (2)/src/pdf_utils.py +300 -0
- abstract_utilities/hash_utils/__init__.py +2 -0
- abstract_utilities/hash_utils/hash_utils.py +5 -0
- abstract_utilities/hash_utils/imports/__init__.py +2 -0
- abstract_utilities/hash_utils/imports/imports.py +1 -0
- abstract_utilities/hash_utils/imports/module_imports.py +0 -0
- abstract_utilities/history_utils/__init__.py +2 -0
- abstract_utilities/history_utils/history_utils.py +37 -0
- abstract_utilities/history_utils/imports/__init__.py +2 -0
- abstract_utilities/history_utils/imports/imports.py +1 -0
- abstract_utilities/history_utils/imports/module_imports.py +0 -0
- abstract_utilities/import_utils/__init__.py +2 -0
- abstract_utilities/import_utils/circular_import_finder.py +222 -0
- abstract_utilities/import_utils/circular_import_finder2.py +118 -0
- abstract_utilities/import_utils/imports/__init__.py +4 -0
- abstract_utilities/import_utils/imports/constants.py +2 -0
- abstract_utilities/import_utils/imports/imports.py +4 -0
- abstract_utilities/import_utils/imports/init_imports.py +3 -0
- abstract_utilities/import_utils/imports/module_imports.py +9 -0
- abstract_utilities/import_utils/imports/utils.py +30 -0
- abstract_utilities/import_utils/src/__init__.py +8 -0
- abstract_utilities/import_utils/src/clean_imports.py +278 -0
- abstract_utilities/import_utils/src/dot_utils.py +80 -0
- abstract_utilities/import_utils/src/extract_utils.py +46 -0
- abstract_utilities/import_utils/src/import_functions.py +110 -0
- abstract_utilities/import_utils/src/import_utils.py +349 -0
- abstract_utilities/import_utils/src/layze_import_utils/__init__.py +2 -0
- abstract_utilities/import_utils/src/layze_import_utils/lazy_utils.py +41 -0
- abstract_utilities/import_utils/src/layze_import_utils/nullProxy.py +37 -0
- abstract_utilities/import_utils/src/nullProxy.py +30 -0
- abstract_utilities/import_utils/src/package_utils/__init__.py +139 -0
- abstract_utilities/import_utils/src/package_utils/context_utils.py +27 -0
- abstract_utilities/import_utils/src/package_utils/import_collectors.py +53 -0
- abstract_utilities/import_utils/src/package_utils/path_utils.py +28 -0
- abstract_utilities/import_utils/src/package_utils/safe_import.py +27 -0
- abstract_utilities/import_utils/src/package_utils.py +140 -0
- abstract_utilities/import_utils/src/package_utilss/__init__.py +139 -0
- abstract_utilities/import_utils/src/package_utilss/context_utils.py +27 -0
- abstract_utilities/import_utils/src/package_utilss/import_collectors.py +53 -0
- abstract_utilities/import_utils/src/package_utilss/path_utils.py +28 -0
- abstract_utilities/import_utils/src/package_utilss/safe_import.py +27 -0
- abstract_utilities/import_utils/src/pkg_utils.py +194 -0
- abstract_utilities/import_utils/src/sysroot_utils.py +112 -0
- abstract_utilities/imports.py +21 -0
- abstract_utilities/json_utils/__init__.py +2 -0
- abstract_utilities/json_utils/imports/__init__.py +2 -0
- abstract_utilities/json_utils/imports/imports.py +2 -0
- abstract_utilities/json_utils/imports/module_imports.py +5 -0
- abstract_utilities/json_utils/json_utils.py +777 -0
- abstract_utilities/list_utils/__init__.py +2 -0
- abstract_utilities/list_utils/imports/__init__.py +2 -0
- abstract_utilities/list_utils/imports/imports.py +1 -0
- abstract_utilities/list_utils/imports/module_imports.py +0 -0
- abstract_utilities/list_utils/list_utils.py +202 -0
- abstract_utilities/log_utils/__init__.py +5 -0
- abstract_utilities/log_utils/abstractLogManager.py +64 -0
- abstract_utilities/log_utils/call_response.py +68 -0
- abstract_utilities/log_utils/imports/__init__.py +2 -0
- abstract_utilities/log_utils/imports/imports.py +7 -0
- abstract_utilities/log_utils/imports/module_imports.py +2 -0
- abstract_utilities/log_utils/log_file.py +162 -0
- abstract_utilities/log_utils/logger_callable.py +49 -0
- abstract_utilities/math_utils/__init__.py +2 -0
- abstract_utilities/math_utils/imports/__init__.py +2 -0
- abstract_utilities/math_utils/imports/imports.py +2 -0
- abstract_utilities/math_utils/imports/module_imports.py +1 -0
- abstract_utilities/math_utils/math_utils.py +208 -0
- abstract_utilities/parse_utils/__init__.py +2 -0
- abstract_utilities/parse_utils/imports/__init__.py +3 -0
- abstract_utilities/parse_utils/imports/constants.py +10 -0
- abstract_utilities/parse_utils/imports/imports.py +2 -0
- abstract_utilities/parse_utils/imports/module_imports.py +4 -0
- abstract_utilities/parse_utils/parse_utils.py +539 -0
- abstract_utilities/path_utils/__init__.py +2 -0
- abstract_utilities/path_utils/imports/__init__.py +3 -0
- abstract_utilities/path_utils/imports/imports.py +1 -0
- abstract_utilities/path_utils/imports/module_imports.py +8 -0
- abstract_utilities/path_utils/path_utils.py +248 -0
- abstract_utilities/path_utils.py +95 -14
- abstract_utilities/read_write_utils/__init__.py +1 -0
- abstract_utilities/read_write_utils/imports/__init__.py +2 -0
- abstract_utilities/read_write_utils/imports/imports.py +2 -0
- abstract_utilities/read_write_utils/imports/module_imports.py +5 -0
- abstract_utilities/read_write_utils/read_write_utils.py +439 -0
- abstract_utilities/read_write_utils.py +113 -62
- abstract_utilities/safe_utils/__init__.py +2 -0
- abstract_utilities/safe_utils/imports/__init__.py +3 -0
- abstract_utilities/safe_utils/imports/imports.py +2 -0
- abstract_utilities/safe_utils/imports/module_imports.py +2 -0
- abstract_utilities/safe_utils/safe_utils.py +166 -0
- abstract_utilities/ssh_utils/__init__.py +3 -1
- abstract_utilities/ssh_utils/classes.py +0 -1
- abstract_utilities/ssh_utils/cmd_utils.py +207 -0
- abstract_utilities/ssh_utils/imports/__init__.py +3 -0
- abstract_utilities/ssh_utils/imports/imports.py +5 -0
- abstract_utilities/ssh_utils/imports/module_imports.py +6 -0
- abstract_utilities/ssh_utils/imports/utils.py +189 -0
- abstract_utilities/ssh_utils/pexpect_utils.py +11 -18
- abstract_utilities/ssh_utils/type_checks.py +92 -0
- abstract_utilities/string_utils/__init__.py +4 -0
- abstract_utilities/string_utils/clean_utils.py +28 -0
- abstract_utilities/string_utils/eat_utils.py +103 -0
- abstract_utilities/string_utils/imports/__init__.py +3 -0
- abstract_utilities/string_utils/imports/imports.py +2 -0
- abstract_utilities/string_utils/imports/module_imports.py +2 -0
- abstract_utilities/string_utils/imports/utils.py +81 -0
- abstract_utilities/string_utils/replace_utils.py +27 -0
- abstract_utilities/string_utils.py +1 -1
- abstract_utilities/thread_utils/__init__.py +2 -0
- abstract_utilities/thread_utils/imports/__init__.py +2 -0
- abstract_utilities/thread_utils/imports/imports.py +2 -0
- abstract_utilities/thread_utils/imports/module_imports.py +2 -0
- abstract_utilities/thread_utils/thread_utils.py +140 -0
- abstract_utilities/time_utils/__init__.py +2 -0
- abstract_utilities/time_utils/imports/__init__.py +2 -0
- abstract_utilities/time_utils/imports/imports.py +3 -0
- abstract_utilities/time_utils/imports/module_imports.py +1 -0
- abstract_utilities/time_utils/time_utils.py +392 -0
- abstract_utilities/type_utils/__init__.py +7 -0
- abstract_utilities/type_utils/alpha_utils.py +59 -0
- abstract_utilities/type_utils/get_type.py +120 -0
- abstract_utilities/type_utils/imports/__init__.py +3 -0
- abstract_utilities/type_utils/imports/constants.py +134 -0
- abstract_utilities/type_utils/imports/imports.py +4 -0
- abstract_utilities/type_utils/imports/module_imports.py +25 -0
- abstract_utilities/type_utils/is_type.py +455 -0
- abstract_utilities/type_utils/make_type.py +126 -0
- abstract_utilities/type_utils/mime_types.py +68 -0
- abstract_utilities/type_utils/num_utils.py +19 -0
- abstract_utilities/type_utils/type_utils.py +104 -0
- {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/METADATA +1 -1
- abstract_utilities-0.2.2.688.dist-info/RECORD +288 -0
- imports/__init__.py +36 -0
- abstract_utilities-0.2.2.480.dist-info/RECORD +0 -92
- {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/WHEEL +0 -0
- {abstract_utilities-0.2.2.480.dist-info → abstract_utilities-0.2.2.688.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# --- auto-package bootstrap (run-safe) ---------------------------------
|
|
2
|
+
from ..imports import *
|
|
3
|
+
from .dot_utils import get_dot_range
|
|
4
|
+
from .sysroot_utils import get_sysroot,get_import_with_sysroot,get_py_files,get_all_py_sysroots
|
|
5
|
+
from .extract_utils import get_all_py_file_paths
|
|
6
|
+
def clean_imports(imports,commaClean=True):
|
|
7
|
+
chars=["*"]
|
|
8
|
+
if not commaClean:
|
|
9
|
+
chars.append(',')
|
|
10
|
+
if isinstance(imports,str):
|
|
11
|
+
imports = imports.split(',')
|
|
12
|
+
return [eatElse(imp,chars=chars) for imp in imports if imp]
|
|
13
|
+
def get_dot_range(import_pkg):
|
|
14
|
+
count = 0
|
|
15
|
+
for char in import_pkg:
|
|
16
|
+
if char != '.':
|
|
17
|
+
break
|
|
18
|
+
count+=1
|
|
19
|
+
return count
|
|
20
|
+
def get_cleaned_import_list(line,commaClean=True):
|
|
21
|
+
cleaned_import_list=[]
|
|
22
|
+
if IMPORT_TAG in line:
|
|
23
|
+
imports = line.split(IMPORT_TAG)[1]
|
|
24
|
+
cleaned_import_list+=clean_imports(imports,commaClean=commaClean)
|
|
25
|
+
return cleaned_import_list
|
|
26
|
+
def get_module_from_import(imp,path=None):
|
|
27
|
+
path = path or os.getcwd()
|
|
28
|
+
i = get_dot_range(imp)
|
|
29
|
+
imp = eatAll(imp,'.')
|
|
30
|
+
sysroot = get_sysroot(path,i)
|
|
31
|
+
return os.path.join(sysroot, imp)
|
|
32
|
+
|
|
33
|
+
def safe_import(name: str, *, package: str | None = None, member: str | None = None, file: str | None = None):
|
|
34
|
+
"""
|
|
35
|
+
Wrapper over importlib.import_module that:
|
|
36
|
+
- if `name` is relative (starts with '.'), ensures `package` is set.
|
|
37
|
+
- if `package` is missing, derives it from `file` (defaults to __file__).
|
|
38
|
+
"""
|
|
39
|
+
file = file or __file__
|
|
40
|
+
ensure_package_context(file)
|
|
41
|
+
if name.startswith(".") and not package:
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
pkg_name = get_module_from_import(name,path=None)
|
|
45
|
+
# also set __package__ if we are running as a script
|
|
46
|
+
if __name__ == "__main__" and (not globals().get("__package__")):
|
|
47
|
+
globals()["__package__"] = pkg_name
|
|
48
|
+
package = pkg_name
|
|
49
|
+
|
|
50
|
+
mod = importlib.import_module(name, package=package)
|
|
51
|
+
return getattr(mod, member) if member else mod
|
|
52
|
+
|
|
53
|
+
def dynamic_import(module_path: str, namespace: dict, all_imports = None):
|
|
54
|
+
"""
|
|
55
|
+
Emulates:
|
|
56
|
+
from module_path import *
|
|
57
|
+
but includes private (_xxx) names too.
|
|
58
|
+
"""
|
|
59
|
+
all_imports = if_none_change(all_imports,True)
|
|
60
|
+
if module_path:
|
|
61
|
+
module = importlib.import_module(module_path)
|
|
62
|
+
# Import literally everything except dunders, unless you want them too.
|
|
63
|
+
names = [n for n in dir(module) if n and ((not all_imports and not n.startswith("_")) or all_imports)]
|
|
64
|
+
for name in names:
|
|
65
|
+
namespace[name] = getattr(module, name)
|
|
66
|
+
return module
|
|
67
|
+
def get_monorepo_root(directory=None,files=None):
|
|
68
|
+
directory = directory or get_initial_caller_dir()
|
|
69
|
+
|
|
70
|
+
py_files = get_all_py_file_paths(directory,add=True)
|
|
71
|
+
sysroots = get_all_py_sysroots(directory=directory,files=py_files)
|
|
72
|
+
monorepo_root = get_common_root(sysroots)
|
|
73
|
+
return monorepo_root
|
|
74
|
+
def switch_to_monorepo_root(directory=None,files=None):
|
|
75
|
+
monorepo_root = get_monorepo_root(directory=directory,files=files)
|
|
76
|
+
if str(monorepo_root) not in sys.path:
|
|
77
|
+
sys.path.insert(0, str(monorepo_root))
|
|
78
|
+
return str(monorepo_root)
|
|
79
|
+
def get_all_imports(directory=None,sysroot=None,globs=None):
|
|
80
|
+
globs = globs or get_true_globals() or globals()
|
|
81
|
+
directory = directory or get_initial_caller_dir()
|
|
82
|
+
files = collect_globs(directory=directory,allowed_exts='.py').get('files')
|
|
83
|
+
sysroot = sysroot or switch_to_monorepo_root(directory=directory,files=files)
|
|
84
|
+
for glo in files:
|
|
85
|
+
imp = get_import_with_sysroot(glo, sysroot)
|
|
86
|
+
dynamic_import(imp, globs)
|
|
87
|
+
def get_all_imports_for_class(self, directory=None, sysroot=None, include_private=True):
|
|
88
|
+
"""
|
|
89
|
+
Load all modules under `directory` and assign their exports as attributes
|
|
90
|
+
on the class instance (self).
|
|
91
|
+
"""
|
|
92
|
+
directory = directory or get_initial_caller_dir()
|
|
93
|
+
files = collect_globs(directory=directory, allowed_exts='.py').get("files")
|
|
94
|
+
|
|
95
|
+
# Compute sysroot (monorepo root)
|
|
96
|
+
sysroot = sysroot or switch_to_monorepo_root(directory=directory, files=files)
|
|
97
|
+
|
|
98
|
+
for glo in files:
|
|
99
|
+
mod_path = get_import_with_sysroot(glo, sysroot)
|
|
100
|
+
module = importlib.import_module(mod_path)
|
|
101
|
+
|
|
102
|
+
for name in dir(module):
|
|
103
|
+
if name.startswith("__"):
|
|
104
|
+
continue
|
|
105
|
+
if not include_private and name.startswith("_"):
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
setattr(self, name, getattr(module, name))
|
|
109
|
+
|
|
110
|
+
return self
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
from ..imports import *
|
|
2
|
+
from .dot_utils import *
|
|
3
|
+
from .sysroot_utils import *
|
|
4
|
+
from .extract_utils import *
|
|
5
|
+
def find_top_package_dir(p: Path) -> Path | None:
|
|
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
|
+
def safe_import(name: str, package: str | None = None, member: str | None = None):
|
|
17
|
+
"""
|
|
18
|
+
Wraps importlib.import_module but also resolves relative imports like '..logPaneTab'.
|
|
19
|
+
If `member` is given, returns that attribute from the module.
|
|
20
|
+
"""
|
|
21
|
+
mod = importlib.import_module(name, package=package)
|
|
22
|
+
return getattr(mod, member) if member else mod
|
|
23
|
+
def unique_module_name(base: str, path: Path) -> str:
|
|
24
|
+
# Make a stable, unique name per file
|
|
25
|
+
digest = hashlib.md5(str(path.resolve()).encode()).hexdigest()[:8]
|
|
26
|
+
stem = path.stem.replace('-', '_')
|
|
27
|
+
return f"_dyn.{base}.{stem}_{digest}"
|
|
28
|
+
|
|
29
|
+
def import_from_path(module_name: str, file_path: Path):
|
|
30
|
+
spec = importlib.util.spec_from_file_location(module_name, str(file_path))
|
|
31
|
+
if spec is None or spec.loader is None:
|
|
32
|
+
raise ImportError(f"Could not load spec for {file_path}")
|
|
33
|
+
mod = importlib.util.module_from_spec(spec)
|
|
34
|
+
# Register before exec to satisfy intra-module relative imports (rare but safe)
|
|
35
|
+
sys.modules[module_name] = mod
|
|
36
|
+
spec.loader.exec_module(mod)
|
|
37
|
+
return mod
|
|
38
|
+
def get_imports(files: List[str],self=None) -> Dict[str, Dict[str, Any]]:
|
|
39
|
+
"""
|
|
40
|
+
Discover package context from each file's relative-import requirements,
|
|
41
|
+
add the proper sysroot to sys.path once, and import by dotted name.
|
|
42
|
+
Returns: {modname: {"filepath", "sysroot", "module", "class", "funcs", "selfs"}}
|
|
43
|
+
"""
|
|
44
|
+
results: Dict[str, Dict[str, Any]] = {}
|
|
45
|
+
seen_sysroots: set[str] = set()
|
|
46
|
+
files = make_list(files)
|
|
47
|
+
for file in files:
|
|
48
|
+
file_p = Path(file).resolve()
|
|
49
|
+
|
|
50
|
+
# 1) Use your logic to find a viable sysroot (top where dots won't exceed)
|
|
51
|
+
sysroot_guess = Path(get_dot_range_sysroot(str(file_p))).resolve()
|
|
52
|
+
# 2) Ensure we import with *package* context: we need the directory ABOVE
|
|
53
|
+
# the topmost package on sys.path, not the package dir itself.
|
|
54
|
+
top_pkg_dir = find_top_package_dir(sysroot_guess) or find_top_package_dir(file_p)
|
|
55
|
+
if not top_pkg_dir:
|
|
56
|
+
raise RuntimeError(f"No package context found for {file_p}; add __init__.py up the tree.")
|
|
57
|
+
sysroot = top_pkg_dir.parent
|
|
58
|
+
|
|
59
|
+
if str(sysroot) not in seen_sysroots:
|
|
60
|
+
ensure_on_path(sysroot)
|
|
61
|
+
seen_sysroots.add(str(sysroot))
|
|
62
|
+
|
|
63
|
+
# 3) Compute dotted name from sysroot and import
|
|
64
|
+
dotted = dotted_from(file_p, sysroot)
|
|
65
|
+
try:
|
|
66
|
+
mod = importlib.import_module(dotted)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
# Helpful hint if user ran file directly (no package context)
|
|
69
|
+
if "__package__" in dir() and not __package__:
|
|
70
|
+
raise RuntimeError(
|
|
71
|
+
f"Import failed for {dotted}. If you ran this file directly, "
|
|
72
|
+
f"bootstrap package context or run via `python -m ...`."
|
|
73
|
+
) from e
|
|
74
|
+
raise
|
|
75
|
+
|
|
76
|
+
# 4) Collect symbols (you can keep your regex readers if you prefer)
|
|
77
|
+
classes = extract_class(str(file_p))
|
|
78
|
+
funcs = extract_funcs(str(file_p))
|
|
79
|
+
selfs = extract_selfs(str(file_p))
|
|
80
|
+
|
|
81
|
+
# stable result key (avoid collisions)
|
|
82
|
+
key = get_unique_name(get_file_parts(str(file_p))['filename'], results)
|
|
83
|
+
|
|
84
|
+
results[key] = {
|
|
85
|
+
"filepath": str(file_p),
|
|
86
|
+
"sysroot": str(sysroot),
|
|
87
|
+
"module": mod,
|
|
88
|
+
"class": classes,
|
|
89
|
+
"funcs": funcs,
|
|
90
|
+
"selfs": selfs,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return results
|
|
94
|
+
def inject_symbols_from_module(
|
|
95
|
+
mod,
|
|
96
|
+
into: dict,
|
|
97
|
+
*,
|
|
98
|
+
expose_functions: bool = True,
|
|
99
|
+
expose_classes: bool = True,
|
|
100
|
+
expose_methods: bool = False,
|
|
101
|
+
self: object | None = None, # <---- NEW
|
|
102
|
+
ctor_overrides: Mapping[str, Tuple[tuple, dict]] | None = None,
|
|
103
|
+
name_prefix: str = "",
|
|
104
|
+
name_filter: Iterable[str] | None = None,
|
|
105
|
+
overwrite: bool = False
|
|
106
|
+
) -> Dict[str, Any]:
|
|
107
|
+
"""
|
|
108
|
+
Inject functions/classes from `mod` into `into` (usually globals()).
|
|
109
|
+
If expose_methods=True and a `self` object is passed, bind instance methods
|
|
110
|
+
directly onto `self` instead of globals().
|
|
111
|
+
"""
|
|
112
|
+
exported: Dict[str, Any] = {}
|
|
113
|
+
ctor_overrides = ctor_overrides or {}
|
|
114
|
+
|
|
115
|
+
def allowed(name: str) -> bool:
|
|
116
|
+
return name_filter is None or name in name_filter
|
|
117
|
+
|
|
118
|
+
# 1) functions
|
|
119
|
+
if expose_functions:
|
|
120
|
+
for name, obj in vars(mod).items():
|
|
121
|
+
input(name)
|
|
122
|
+
if not allowed(name):
|
|
123
|
+
continue
|
|
124
|
+
if inspect.isfunction(obj) and obj.__module__ == mod.__name__:
|
|
125
|
+
out_name = f"{name_prefix}{name}"
|
|
126
|
+
if overwrite or out_name not in into:
|
|
127
|
+
into[out_name] = obj
|
|
128
|
+
exported[out_name] = obj
|
|
129
|
+
|
|
130
|
+
# 2) classes (+ optional bound methods)
|
|
131
|
+
if expose_classes or expose_methods:
|
|
132
|
+
for cls_name, cls in vars(mod).items():
|
|
133
|
+
if not allowed(cls_name):
|
|
134
|
+
continue
|
|
135
|
+
if inspect.isclass(cls) and cls.__module__ == mod.__name__:
|
|
136
|
+
if expose_classes:
|
|
137
|
+
out_name = f"{name_prefix}{cls_name}"
|
|
138
|
+
if overwrite or out_name not in into:
|
|
139
|
+
into[out_name] = cls
|
|
140
|
+
exported[out_name] = cls
|
|
141
|
+
|
|
142
|
+
if expose_methods and self is not None:
|
|
143
|
+
# instantiate class
|
|
144
|
+
args, kwargs = ctor_overrides.get(cls_name, ((), {}))
|
|
145
|
+
try:
|
|
146
|
+
inst = cls(*args, **kwargs)
|
|
147
|
+
except Exception:
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
for meth_name, meth_obj in vars(cls).items():
|
|
151
|
+
if meth_name.startswith("__"):
|
|
152
|
+
continue
|
|
153
|
+
if inspect.isfunction(meth_obj) or inspect.ismethoddescriptor(meth_obj):
|
|
154
|
+
try:
|
|
155
|
+
bound = getattr(inst, meth_name)
|
|
156
|
+
except Exception:
|
|
157
|
+
continue
|
|
158
|
+
if callable(bound):
|
|
159
|
+
# attach directly to the `self` you passed in
|
|
160
|
+
if not hasattr(self, meth_name) or overwrite:
|
|
161
|
+
setattr(self, meth_name, bound)
|
|
162
|
+
exported[f"{cls_name}.{meth_name}"] = bound
|
|
163
|
+
|
|
164
|
+
return exported
|
|
165
|
+
def inject_from_imports_map(
|
|
166
|
+
imports_map: Dict[str, Dict[str, Any]],
|
|
167
|
+
*,
|
|
168
|
+
into: Optional[dict] = None, # where to put free funcs/classes (defaults to this module's globals)
|
|
169
|
+
attach_self: Optional[object] = None, # bind names listed in "selfs" onto this object
|
|
170
|
+
prefix_modules: bool = False, # add "<module>__" prefix to avoid collisions
|
|
171
|
+
overwrite: bool = False, # allow overwriting existing names
|
|
172
|
+
self:any=None
|
|
173
|
+
) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
Emulates: from functions import * (plus: bind 'self' methods)
|
|
176
|
+
Returns dict of injected_name -> object (including methods bound to attach_self).
|
|
177
|
+
"""
|
|
178
|
+
ns = into if into is not None else globals()
|
|
179
|
+
exported: Dict[str, Any] = {}
|
|
180
|
+
|
|
181
|
+
for mod_key, info in imports_map.items():
|
|
182
|
+
mod = info.get("module")
|
|
183
|
+
if mod is None:
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
func_names: List[str] = info.get("funcs", [])
|
|
187
|
+
self_names: List[str] = info.get("selfs", [])
|
|
188
|
+
class_names: List[str] = info.get("class", [])
|
|
189
|
+
|
|
190
|
+
# 1) bind the "selfs" directly onto self
|
|
191
|
+
if self is not None:
|
|
192
|
+
for name in self_names:
|
|
193
|
+
func = getattr(mod, name, None)
|
|
194
|
+
if not callable(func):
|
|
195
|
+
continue
|
|
196
|
+
# sanity: ensure first param is literally named 'self'
|
|
197
|
+
try:
|
|
198
|
+
params = list(inspect.signature(func).parameters.values())
|
|
199
|
+
if not params or params[0].name != "self":
|
|
200
|
+
continue
|
|
201
|
+
except Exception:
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
bound = MethodType(func, self)
|
|
205
|
+
if overwrite or not hasattr(self, name):
|
|
206
|
+
setattr(self, name, bound)
|
|
207
|
+
exported[f"{mod_key}.self.{name}"] = bound
|
|
208
|
+
|
|
209
|
+
# 2) Inject free functions (exclude the ones we just bound to self to avoid dupes)
|
|
210
|
+
for name in func_names:
|
|
211
|
+
if name in self_names:
|
|
212
|
+
continue
|
|
213
|
+
obj = getattr(mod, name, None)
|
|
214
|
+
if inspect.isfunction(obj) and obj.__module__ == mod.__name__:
|
|
215
|
+
out_name = f"{mod_key}__{name}" if prefix_modules else name
|
|
216
|
+
if overwrite or out_name not in ns:
|
|
217
|
+
ns[out_name] = obj
|
|
218
|
+
exported[out_name] = obj
|
|
219
|
+
|
|
220
|
+
# 3) Inject classes
|
|
221
|
+
for name in class_names:
|
|
222
|
+
cls = getattr(mod, name, None)
|
|
223
|
+
if inspect.isclass(cls) and cls.__module__ == mod.__name__:
|
|
224
|
+
out_name = f"{mod_key}__{name}" if prefix_modules else name
|
|
225
|
+
if overwrite or out_name not in ns:
|
|
226
|
+
ns[out_name] = cls
|
|
227
|
+
exported[out_name] = cls
|
|
228
|
+
|
|
229
|
+
# optional: control wildcard export from THIS module
|
|
230
|
+
ns["__all__"] = sorted(set(list(ns.get("__all__", [])) + list(exported.keys())))
|
|
231
|
+
return exported
|
|
232
|
+
def ifFunctionsInFiles(root: str | None = None, *, expose_methods: bool = True, self=None) -> Dict[str, Any]:
|
|
233
|
+
here = Path(__file__).resolve().parent
|
|
234
|
+
base = Path(root).resolve() if root else here
|
|
235
|
+
candidates = [base / "functions", base / "functions.py"]
|
|
236
|
+
files: List[str] = []
|
|
237
|
+
for item in candidates:
|
|
238
|
+
if item.exists():
|
|
239
|
+
if item.is_dir():
|
|
240
|
+
files = [str(p) for p in item.rglob("*.py") if p.name != "__init__.py"]
|
|
241
|
+
else:
|
|
242
|
+
files = [str(item)]
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
exported_all: Dict[str, Any] = {}
|
|
246
|
+
imports_map = get_imports(files)
|
|
247
|
+
|
|
248
|
+
exported = inject_from_imports_map(
|
|
249
|
+
imports_map,
|
|
250
|
+
self=self
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return exported
|
|
254
|
+
def attach_self_functions(
|
|
255
|
+
imports_map: Dict[str, Dict[str, Any]],
|
|
256
|
+
self_obj: object,
|
|
257
|
+
*,
|
|
258
|
+
overwrite: bool = False,
|
|
259
|
+
only: Optional[List[str]] = None, # whitelist of method names to attach
|
|
260
|
+
prefix_modules: bool = False # attach as core_utils__on_run_code instead of on_run_code
|
|
261
|
+
) -> Dict[str, Any]:
|
|
262
|
+
"""
|
|
263
|
+
For each module in imports_map, bind names listed in 'selfs' (top-level defs whose
|
|
264
|
+
first param is 'self') directly onto `self_obj`.
|
|
265
|
+
|
|
266
|
+
Returns {attached_name: bound_method}.
|
|
267
|
+
"""
|
|
268
|
+
attached: Dict[str, Any] = {}
|
|
269
|
+
|
|
270
|
+
for mod_key, info in imports_map.items():
|
|
271
|
+
mod = info.get("module")
|
|
272
|
+
if not mod:
|
|
273
|
+
continue
|
|
274
|
+
|
|
275
|
+
self_names: List[str] = info.get("selfs", [])
|
|
276
|
+
for name in self_names:
|
|
277
|
+
if only is not None and name not in only:
|
|
278
|
+
continue
|
|
279
|
+
|
|
280
|
+
func = getattr(mod, name, None)
|
|
281
|
+
if not callable(func):
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
# sanity check: first param literally named 'self'
|
|
285
|
+
try:
|
|
286
|
+
params = list(inspect.signature(func).parameters.values())
|
|
287
|
+
if not params or params[0].name != "self":
|
|
288
|
+
continue
|
|
289
|
+
except Exception:
|
|
290
|
+
continue
|
|
291
|
+
|
|
292
|
+
bound = MethodType(func, self_obj)
|
|
293
|
+
out_name = f"{mod_key}__{name}" if prefix_modules else name
|
|
294
|
+
|
|
295
|
+
if overwrite or not hasattr(self_obj, out_name):
|
|
296
|
+
setattr(self_obj, out_name, bound)
|
|
297
|
+
attached[out_name] = bound
|
|
298
|
+
|
|
299
|
+
return attached
|
|
300
|
+
def initFuncs(self, mode=None):
|
|
301
|
+
"""
|
|
302
|
+
Load functions in either dev (dynamic) or prod (static) mode.
|
|
303
|
+
"""
|
|
304
|
+
mode = mode or "dev"
|
|
305
|
+
|
|
306
|
+
logger = get_logFile(__name__)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
base_pkg = self.__class__.__module__.rsplit('.', 1)[0]
|
|
310
|
+
|
|
311
|
+
if mode == "prod":
|
|
312
|
+
# All imports come from the auto-generated import script
|
|
313
|
+
try:
|
|
314
|
+
imports_mod = importlib.import_module(f"{base_pkg}.imports.funcs_imports")
|
|
315
|
+
|
|
316
|
+
# Attach all exports
|
|
317
|
+
|
|
318
|
+
for name in getattr(imports_mod, "__all__", []):
|
|
319
|
+
try:
|
|
320
|
+
func = getattr(imports_mod, name)
|
|
321
|
+
setattr(self, name, func.__get__(self))
|
|
322
|
+
except Exception as e:
|
|
323
|
+
logger.info(f"initFuncs({mode}) imports_mod error: {e}")
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.info(f"initFuncs({mode}) base_pkg error: {e}")
|
|
328
|
+
return self
|
|
329
|
+
# --- DEV MODE: your existing dynamic loader ---
|
|
330
|
+
import inspect, pkgutil, importlib
|
|
331
|
+
try:
|
|
332
|
+
pkg = importlib.import_module(base_pkg + ".functions")
|
|
333
|
+
|
|
334
|
+
existing = set(self.__dict__) | set(dir(self))
|
|
335
|
+
|
|
336
|
+
for _, module_name, _ in pkgutil.iter_modules(pkg.__path__):
|
|
337
|
+
try:
|
|
338
|
+
mod = importlib.import_module(f"{pkg.__name__}.{module_name}")
|
|
339
|
+
|
|
340
|
+
for name, obj in inspect.getmembers(mod, inspect.isfunction):
|
|
341
|
+
if name not in existing:
|
|
342
|
+
setattr(self, name, obj.__get__(self))
|
|
343
|
+
except Exception as e:
|
|
344
|
+
logger.info(f"initFuncs({mode}) pkgutil error: {e}")
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
except Exception as e:
|
|
348
|
+
logger.info(f"initFuncs({mode}) importlib error: {e}")
|
|
349
|
+
return self
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from ...imports import *
|
|
2
|
+
from .nullProxy import nullProxy,nullProxy_logger
|
|
3
|
+
@lru_cache(maxsize=None)
|
|
4
|
+
def lazy_import_single(name: str,fallback=None):
|
|
5
|
+
"""
|
|
6
|
+
Import module safely. If unavailable, return NullProxy.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
if name in sys.modules:
|
|
10
|
+
return sys.modules[name]
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
module = importlib.import_module(name)
|
|
14
|
+
return module
|
|
15
|
+
except Exception as e:
|
|
16
|
+
nullProxy_logger.warning(
|
|
17
|
+
"[lazy_import] Failed to import '%s': %s",
|
|
18
|
+
name,
|
|
19
|
+
e,
|
|
20
|
+
)
|
|
21
|
+
return nullProxy(name,fallback=fallback)
|
|
22
|
+
|
|
23
|
+
def get_lazy_attr(module_name: str, *attrs,fallback=None):
|
|
24
|
+
obj = lazy_import(module_name,fallback=fallback)
|
|
25
|
+
|
|
26
|
+
for attr in attrs:
|
|
27
|
+
try:
|
|
28
|
+
obj = getattr(obj, attr)
|
|
29
|
+
except Exception:
|
|
30
|
+
return nullProxy(module_name, attrs,fallback=fallback)
|
|
31
|
+
|
|
32
|
+
return obj
|
|
33
|
+
def lazy_import(name: str, *attrs,fallback=None):
|
|
34
|
+
"""
|
|
35
|
+
Import module safely. If unavailable, return NullProxy.
|
|
36
|
+
"""
|
|
37
|
+
if attrs:
|
|
38
|
+
obj = get_lazy_attr(name, *attrs,fallback=fallback)
|
|
39
|
+
else:
|
|
40
|
+
obj = lazy_import_single(name,fallback=fallback)
|
|
41
|
+
return obj
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from ...imports import *
|
|
2
|
+
nullProxy_logger = logging.getLogger("abstract.lazy_import")
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class nullProxy:
|
|
6
|
+
"""
|
|
7
|
+
Safe, chainable, callable placeholder for missing modules/attributes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, name, path=(),fallback=None):
|
|
11
|
+
self._name = name
|
|
12
|
+
self._path = path
|
|
13
|
+
self.fallback=fallback
|
|
14
|
+
def __getattr__(self, attr):
|
|
15
|
+
return nullProxy(self._name, self._path + (attr,))
|
|
16
|
+
|
|
17
|
+
def __call__(self, *args, **kwargs):
|
|
18
|
+
if self.fallback is not None:
|
|
19
|
+
try:
|
|
20
|
+
return self.fallback(*args, **kwargs)
|
|
21
|
+
except Exception as e:
|
|
22
|
+
logger.info(f"{e}")
|
|
23
|
+
nullProxy_logger.warning(
|
|
24
|
+
"[lazy_import] Call to missing module/attr: %s.%s args=%s kwargs=%s",
|
|
25
|
+
self._name,
|
|
26
|
+
".".join(self._path),
|
|
27
|
+
args,
|
|
28
|
+
kwargs,
|
|
29
|
+
)
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
full = ".".join((self._name, *self._path))
|
|
34
|
+
return f"<nullProxy {full}>"
|
|
35
|
+
|
|
36
|
+
def __bool__(self):
|
|
37
|
+
return False # safe in conditionals
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from ...imports import *
|
|
2
|
+
lazy_import_logger = logging.getLogger("abstract.lazy_import")
|
|
3
|
+
class nullProxy:
|
|
4
|
+
"""
|
|
5
|
+
Safe, chainable, callable placeholder for missing modules/attributes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, name, path=()):
|
|
9
|
+
self._name = name
|
|
10
|
+
self._path = path
|
|
11
|
+
|
|
12
|
+
def __getattr__(self, attr):
|
|
13
|
+
return nullProxy(self._name, self._path + (attr,))
|
|
14
|
+
|
|
15
|
+
def __call__(self, *args, **kwargs):
|
|
16
|
+
lazy_import_logger.warning(
|
|
17
|
+
"[lazy_import] Call to missing module/attr: %s.%s args=%s kwargs=%s",
|
|
18
|
+
self._name,
|
|
19
|
+
".".join(self._path),
|
|
20
|
+
args,
|
|
21
|
+
kwargs,
|
|
22
|
+
)
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
def __repr__(self):
|
|
26
|
+
full = ".".join((self._name, *self._path))
|
|
27
|
+
return f"<nullProxy {full}>"
|
|
28
|
+
|
|
29
|
+
def __bool__(self):
|
|
30
|
+
return False # safe in conditionals
|