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
abstract_utilities/__init__.py
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
from datetime import timedelta
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from typing import *
|
|
1
|
+
from .imports import *
|
|
5
2
|
from .hash_utils import *
|
|
6
3
|
##from .dynimport import get_abstract_import,import_symbols_to_parent,call_for_all_tabs
|
|
7
|
-
from .robust_readers import *
|
|
8
|
-
from .dynimport import get_abstract_import
|
|
9
4
|
from .json_utils import (unified_json_loader,
|
|
10
5
|
find_keys,
|
|
11
6
|
get_key_values_from_path,
|
|
@@ -37,10 +32,11 @@ from .json_utils import (unified_json_loader,
|
|
|
37
32
|
safe_write_to_file,
|
|
38
33
|
safe_save_updated_json_data,
|
|
39
34
|
get_result_from_data,
|
|
40
|
-
flatten_json
|
|
35
|
+
flatten_json,
|
|
36
|
+
to_json_safe
|
|
41
37
|
)
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
|
|
39
|
+
from .directory_utils import *
|
|
44
40
|
from .path_utils import *
|
|
45
41
|
from .file_utils import *
|
|
46
42
|
from .list_utils import (get_highest_value_obj,
|
|
@@ -50,7 +46,9 @@ from .list_utils import (get_highest_value_obj,
|
|
|
50
46
|
compare_lists,
|
|
51
47
|
get_symetric_difference,
|
|
52
48
|
list_set,
|
|
53
|
-
make_list_it
|
|
49
|
+
make_list_it,
|
|
50
|
+
get_single_from_list
|
|
51
|
+
)
|
|
54
52
|
from .time_utils import (get_time_stamp,
|
|
55
53
|
get_sleep,
|
|
56
54
|
sleep_count_down,
|
|
@@ -61,7 +59,7 @@ from .time_utils import (get_time_stamp,
|
|
|
61
59
|
parse_timestamp,
|
|
62
60
|
get_time_now_iso,
|
|
63
61
|
is_valid_time)
|
|
64
|
-
from .
|
|
62
|
+
from .string_utils import (eatInner,
|
|
65
63
|
eatAll,
|
|
66
64
|
eatOuter,
|
|
67
65
|
url_join,
|
|
@@ -88,7 +86,8 @@ from .type_utils import (make_bool,
|
|
|
88
86
|
get_numbers_comp,
|
|
89
87
|
is_any_instance,
|
|
90
88
|
break_string,
|
|
91
|
-
MIME_TYPES
|
|
89
|
+
MIME_TYPES,
|
|
90
|
+
get_if_None
|
|
92
91
|
)
|
|
93
92
|
get_media_types = get_all_types = get_all_file_types
|
|
94
93
|
from .math_utils import (convert_to_percentage,
|
|
@@ -106,20 +105,29 @@ from .compare_utils import (create_new_name,
|
|
|
106
105
|
best_match)
|
|
107
106
|
from .thread_utils import ThreadManager
|
|
108
107
|
from .history_utils import HistoryManager
|
|
109
|
-
|
|
108
|
+
|
|
110
109
|
from .parse_utils import (num_tokens_from_string,
|
|
111
110
|
chunk_source_code,
|
|
112
111
|
chunk_any_to_tokens,
|
|
113
112
|
detect_language_from_text,
|
|
114
113
|
chunk_by_language_context,
|
|
115
|
-
search_code
|
|
114
|
+
search_code,
|
|
115
|
+
get_within_quotes)
|
|
116
116
|
|
|
117
117
|
from .log_utils import get_caller_info,get_logFile,print_or_log,get_json_call_response,initialize_call_log
|
|
118
118
|
from .error_utils import try_func
|
|
119
|
+
from .class_utils import *
|
|
119
120
|
from .ssh_utils import *
|
|
120
121
|
from .env_utils import *
|
|
121
122
|
from .path_utils import *
|
|
122
123
|
from .file_utils import *
|
|
123
|
-
from .file_utils import call_for_all_tabs
|
|
124
124
|
from .string_utils import *
|
|
125
|
-
from .
|
|
125
|
+
from .import_utils import *
|
|
126
|
+
from .read_write_utils import (read_from_file,
|
|
127
|
+
write_to_file,
|
|
128
|
+
make_dirs,
|
|
129
|
+
make_dirs,
|
|
130
|
+
copy_files,
|
|
131
|
+
make_path,
|
|
132
|
+
run_cmd
|
|
133
|
+
)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from abstract_utilities import *
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
def clean_line(line):
|
|
4
|
+
return eatAll(line,[' ','','\t','\n'])
|
|
5
|
+
def is_from_line_group(line):
|
|
6
|
+
if line and line.startswith(FROM_TAG) and IMPORT_TAG in line and '(' in line:
|
|
7
|
+
import_spl = line.split(IMPORT_TAG)[-1]
|
|
8
|
+
import_spl_clean = clean_line(line)
|
|
9
|
+
if not import_spl_clean.endswith(')'):
|
|
10
|
+
return True
|
|
11
|
+
return False
|
|
12
|
+
def clean_imports(text=None,file_path=None,import_pkg_js=None,fill_nulines=False):
|
|
13
|
+
if text and os.path.isfile(text):
|
|
14
|
+
file_path = text
|
|
15
|
+
input(file_path)
|
|
16
|
+
text = read_from_file(file_path)
|
|
17
|
+
if not import_pkg_js:
|
|
18
|
+
import_pkg_js = get_all_imports(text=text,file_path=file_path)
|
|
19
|
+
import_pkg_js = ensure_import_pkg_js(import_pkg_js,file_path=file_path)
|
|
20
|
+
nu_lines = import_pkg_js["context"]["nulines"]
|
|
21
|
+
for pkg,values in import_pkg_js.items():
|
|
22
|
+
comments = []
|
|
23
|
+
if pkg not in ["context"]:
|
|
24
|
+
|
|
25
|
+
imports = values.get('imports')
|
|
26
|
+
for i,imp in enumerate(imports):
|
|
27
|
+
if '#' in imp:
|
|
28
|
+
imp_spl = imp.split('#')
|
|
29
|
+
comments.append(imp_spl[-1])
|
|
30
|
+
imports[i] = clean_line(imp_spl[0])
|
|
31
|
+
imports = list(set(imports))
|
|
32
|
+
if '*' in imports:
|
|
33
|
+
imports="*"
|
|
34
|
+
else:
|
|
35
|
+
imports=','.join(imports)
|
|
36
|
+
if comments:
|
|
37
|
+
comments=','.join(comments)
|
|
38
|
+
imports+=f" #{comments}"
|
|
39
|
+
import_pkg_js[pkg]["imports"]=imports
|
|
40
|
+
if fill_nulines:
|
|
41
|
+
line = values.get('line')
|
|
42
|
+
if len(nu_lines) >= line:
|
|
43
|
+
nu_lines[line] += imports
|
|
44
|
+
return import_pkg_js
|
|
45
|
+
def get_all_imports(text=None,file_path=None,import_pkg_js=None):
|
|
46
|
+
if text and os.path.isfile(text):
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
text = read_from_file(text)
|
|
50
|
+
except:
|
|
51
|
+
pass
|
|
52
|
+
file_path = text
|
|
53
|
+
text = get_text_or_read(text=text,file_path=file_path)
|
|
54
|
+
lines = text.split('\n')
|
|
55
|
+
cleaned_import_list=[]
|
|
56
|
+
nu_lines = []
|
|
57
|
+
is_from_group = False
|
|
58
|
+
import_pkg_js = ensure_import_pkg_js(import_pkg_js,file_path=file_path)
|
|
59
|
+
for line in lines:
|
|
60
|
+
if line.startswith(IMPORT_TAG) and ' from ' not in line:
|
|
61
|
+
cleaned_import_list = get_cleaned_import_list(line)
|
|
62
|
+
import_pkg_js = add_imports_to_import_pkg_js("import",cleaned_import_list,import_pkg_js=import_pkg_js)
|
|
63
|
+
else:
|
|
64
|
+
if is_from_group:
|
|
65
|
+
import_pkg=is_from_group
|
|
66
|
+
line = clean_line(line)
|
|
67
|
+
if line.endswith(')'):
|
|
68
|
+
is_from_group=False
|
|
69
|
+
line=line[:-1]
|
|
70
|
+
imports_from_import_pkg = clean_imports(line)
|
|
71
|
+
import_pkg_js = add_imports_to_import_pkg_js(import_pkg,imports_from_import_pkg,import_pkg_js=import_pkg_js)
|
|
72
|
+
|
|
73
|
+
else:
|
|
74
|
+
import_pkg_js=update_import_pkg_js(line,import_pkg_js=import_pkg_js)
|
|
75
|
+
if is_from_line_group(line) and is_from_group == False:
|
|
76
|
+
is_from_group=get_import_pkg(line)
|
|
77
|
+
return import_pkg_js
|
|
78
|
+
def get_path_or_init(pkg_info):
|
|
79
|
+
root_dirname = pkg_info.get("root_dirname")
|
|
80
|
+
pkg = pkg_info.get("pkg")
|
|
81
|
+
rel_path = pkg.replace('.','/')
|
|
82
|
+
dirname = os.path.dirname(root_dirname)
|
|
83
|
+
pkg_path = os.path.join(dirname,rel_path)
|
|
84
|
+
pkg_py_path = f"{pkg_path}.py"
|
|
85
|
+
if os.path.isfile(pkg_py_path):
|
|
86
|
+
return pkg_py_path
|
|
87
|
+
pkg_init_path = os.path.join(pkg_path,'__init__.py')
|
|
88
|
+
if os.path.isdir(pkg_path):
|
|
89
|
+
if os.path.isfile(pkg_init_path):
|
|
90
|
+
return pkg_init_path
|
|
91
|
+
#input(f"nnot found == {pkg_info}")
|
|
92
|
+
def get_dot_fro_line(line,dirname=None,file_path=None,get_info=False):
|
|
93
|
+
info_js = {"nuline":line,"og_line":line,"pkg":line,"dirname":dirname,"file_path":file_path,"root_dirname":None,"local":False}
|
|
94
|
+
if dirname and is_file(dirname):
|
|
95
|
+
file_path=dirname
|
|
96
|
+
dirname = os.path.dirname(dirname)
|
|
97
|
+
info_js["file_path"]=file_path
|
|
98
|
+
info_js["dirname"]=dirname
|
|
99
|
+
from_line = line.split(FROM_TAG)[-1]
|
|
100
|
+
dot_fro = ""
|
|
101
|
+
for char in from_line:
|
|
102
|
+
if char != '.':
|
|
103
|
+
pkg = f"{dot_fro}{eatAll(from_line,'.')}"
|
|
104
|
+
nuline=f"from {pkg}"
|
|
105
|
+
info_js["nuline"]=nuline
|
|
106
|
+
info_js["pkg"]=pkg
|
|
107
|
+
break
|
|
108
|
+
if dirname:
|
|
109
|
+
info_js["root_dirname"]=dirname
|
|
110
|
+
dirbase = os.path.basename(dirname)
|
|
111
|
+
dirname = os.path.dirname(dirname)
|
|
112
|
+
|
|
113
|
+
dot_fro = f"{dirbase}.{dot_fro}"
|
|
114
|
+
if get_info:
|
|
115
|
+
if dot_fro and os.path.isdir(info_js["root_dirname"]):
|
|
116
|
+
info_js["local"]=True
|
|
117
|
+
info_js["pkg_path"]=get_path_or_init(info_js)
|
|
118
|
+
return info_js
|
|
119
|
+
return line
|
|
120
|
+
def get_top_level_imp(line,dirname=None):
|
|
121
|
+
imp = get_dot_fro_line(line,dirname)
|
|
122
|
+
return imp.split('.')[0]
|
|
123
|
+
def return_local_imps(file_path):
|
|
124
|
+
local_imps = []
|
|
125
|
+
dirname = os.path.dirname(file_path)
|
|
126
|
+
imports_js = get_all_imports(file_path)
|
|
127
|
+
for pkg,imps in imports_js.items():
|
|
128
|
+
if pkg not in ['context','nulines']:
|
|
129
|
+
full_imp_info = get_dot_fro_line(pkg,dirname,file_path=file_path,get_info=True)
|
|
130
|
+
if full_imp_info.get("local") == True:
|
|
131
|
+
local_imps.append(full_imp_info)
|
|
132
|
+
return local_imps
|
|
133
|
+
def get_all_pkg_paths(file_path):
|
|
134
|
+
pkg_paths = []
|
|
135
|
+
local_imps = return_local_imps(file_path)
|
|
136
|
+
for local_imp in local_imps:
|
|
137
|
+
curr_file_path = local_imp.get('file_path')
|
|
138
|
+
pkg_path = local_imp.get('pkg_path')
|
|
139
|
+
if pkg_path != None:
|
|
140
|
+
pkg_paths.append(pkg_path)
|
|
141
|
+
return pkg_paths
|
|
142
|
+
def get_cir_dir(pkg_path):
|
|
143
|
+
dirname = os.path.dirname(pkg_path)
|
|
144
|
+
dirbase = os.path.basename(dirname)
|
|
145
|
+
while True:
|
|
146
|
+
if dirname == "/home/flerb/Documents/pythonTools/modules/src/modules/abstract_utilities/src/abstract_utilities":
|
|
147
|
+
break
|
|
148
|
+
dirbase = os.path.basename(dirname)
|
|
149
|
+
dirname = os.path.dirname(dirname)
|
|
150
|
+
#input(f"{dirbase} is circular")
|
|
151
|
+
return dirbase
|
|
152
|
+
def is_circular(pkg_path):
|
|
153
|
+
pkg_paths = get_all_pkg_paths(pkg_path)
|
|
154
|
+
if pkg_path in pkg_paths:
|
|
155
|
+
return pkg_path
|
|
156
|
+
def are_circular(pkg_path,cir_dirs = None):
|
|
157
|
+
cir_dirs = cir_dirs or []
|
|
158
|
+
pkg_path = is_circular(pkg_path)
|
|
159
|
+
if pkg_path:
|
|
160
|
+
if pkg_path not in cir_dirs:
|
|
161
|
+
cir_dirs.append(pkg_path)
|
|
162
|
+
return cir_dirs
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def build_dependency_graph(main_directory):
|
|
166
|
+
"""Map each file to all local imports (by resolved pkg_path)."""
|
|
167
|
+
graph = defaultdict(list)
|
|
168
|
+
dirs, all_local_scripts = get_files_and_dirs(
|
|
169
|
+
main_directory,
|
|
170
|
+
allowed_exts='.py',
|
|
171
|
+
exclude_dirs=['depriciate', 'junk'],
|
|
172
|
+
files_only=True
|
|
173
|
+
)
|
|
174
|
+
for file_path in all_local_scripts:
|
|
175
|
+
deps = get_all_pkg_paths(file_path)
|
|
176
|
+
for dep in deps:
|
|
177
|
+
if dep and os.path.isfile(dep):
|
|
178
|
+
graph[file_path].append(dep)
|
|
179
|
+
return graph
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def find_circular_chains(graph):
|
|
183
|
+
"""Detect circular imports and return their full dependency paths."""
|
|
184
|
+
visited, cycles = set(), []
|
|
185
|
+
|
|
186
|
+
def dfs(node, path):
|
|
187
|
+
visited.add(node)
|
|
188
|
+
path.append(node)
|
|
189
|
+
for dep in graph.get(node, []):
|
|
190
|
+
if dep not in path:
|
|
191
|
+
dfs(dep, path.copy())
|
|
192
|
+
else:
|
|
193
|
+
# Found a circular import
|
|
194
|
+
cycle_start = path.index(dep)
|
|
195
|
+
cycle = path[cycle_start:] + [dep]
|
|
196
|
+
if cycle not in cycles:
|
|
197
|
+
cycles.append(cycle)
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
for start in graph:
|
|
201
|
+
dfs(start, [])
|
|
202
|
+
return cycles
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def explain_circular_imports(cycles):
|
|
206
|
+
"""Pretty-print circular import chains with file names and import lines."""
|
|
207
|
+
for i, cycle in enumerate(cycles, 1):
|
|
208
|
+
print(f"\n🔁 Circular import {i}:")
|
|
209
|
+
for j in range(len(cycle) - 1):
|
|
210
|
+
src, dst = cycle[j], cycle[j + 1]
|
|
211
|
+
print(f" {os.path.basename(src)} → {os.path.basename(dst)}")
|
|
212
|
+
print(f" ^ back to {os.path.basename(cycle[0])}")
|
|
213
|
+
main_directory = "/home/flerb/Documents/pythonTools/modules/src/modules/abstract_utilities/src/abstract_utilities"
|
|
214
|
+
|
|
215
|
+
graph = build_dependency_graph(main_directory)
|
|
216
|
+
cycles = find_circular_chains(graph)
|
|
217
|
+
|
|
218
|
+
if not cycles:
|
|
219
|
+
print("✅ No circular imports found.")
|
|
220
|
+
else:
|
|
221
|
+
print(f"❌ Found {len(cycles)} circular import(s).")
|
|
222
|
+
explain_circular_imports(cycles)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from abstract_utilities import *
|
|
2
|
+
import os
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
def get_path_or_init(pkg_info):
|
|
6
|
+
root_dirname = pkg_info.get("root_dirname")
|
|
7
|
+
pkg = pkg_info.get("pkg")
|
|
8
|
+
rel_path = pkg.replace('.', '/')
|
|
9
|
+
dirname = os.path.dirname(root_dirname)
|
|
10
|
+
pkg_path = os.path.join(dirname, rel_path)
|
|
11
|
+
pkg_py_path = f"{pkg_path}.py"
|
|
12
|
+
if os.path.isfile(pkg_py_path):
|
|
13
|
+
return pkg_py_path
|
|
14
|
+
pkg_init_path = os.path.join(pkg_path, '__init__.py')
|
|
15
|
+
if os.path.isdir(pkg_path) and os.path.isfile(pkg_init_path):
|
|
16
|
+
return pkg_init_path
|
|
17
|
+
# optional: silence instead of blocking input()
|
|
18
|
+
print(f"⚠️ not found == {pkg_info}")
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_dot_fro_line(line, dirname=None, file_path=None, get_info=False):
|
|
23
|
+
info_js = {"nuline": line, "og_line": line, "pkg": line, "dirname": dirname,
|
|
24
|
+
"file_path": file_path, "root_dirname": None, "local": False}
|
|
25
|
+
if dirname and is_file(dirname):
|
|
26
|
+
file_path = dirname
|
|
27
|
+
dirname = os.path.dirname(dirname)
|
|
28
|
+
info_js["file_path"] = file_path
|
|
29
|
+
info_js["dirname"] = dirname
|
|
30
|
+
|
|
31
|
+
from_line = line.split(FROM_TAG)[-1]
|
|
32
|
+
dot_fro = ""
|
|
33
|
+
for char in from_line:
|
|
34
|
+
if char != '.':
|
|
35
|
+
pkg = f"{dot_fro}{eatAll(from_line, '.')}"
|
|
36
|
+
nuline = f"from {pkg}"
|
|
37
|
+
info_js["nuline"] = nuline
|
|
38
|
+
info_js["pkg"] = pkg
|
|
39
|
+
break
|
|
40
|
+
if dirname:
|
|
41
|
+
info_js["root_dirname"] = dirname
|
|
42
|
+
dirbase = os.path.basename(dirname)
|
|
43
|
+
dirname = os.path.dirname(dirname)
|
|
44
|
+
dot_fro = f"{dirbase}.{dot_fro}"
|
|
45
|
+
|
|
46
|
+
if get_info:
|
|
47
|
+
if dot_fro and os.path.isdir(info_js.get("root_dirname") or ""):
|
|
48
|
+
info_js["local"] = True
|
|
49
|
+
info_js["pkg_path"] = get_path_or_init(info_js)
|
|
50
|
+
return info_js
|
|
51
|
+
return line
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def return_local_imps(file_path):
|
|
55
|
+
local_imps = []
|
|
56
|
+
dirname = os.path.dirname(file_path)
|
|
57
|
+
imports_js = get_all_imports(file_path)
|
|
58
|
+
for pkg, imps in imports_js.items():
|
|
59
|
+
if pkg not in ['context', 'nulines']:
|
|
60
|
+
full_imp_info = get_dot_fro_line(pkg, dirname, file_path=file_path, get_info=True)
|
|
61
|
+
if full_imp_info.get("local"):
|
|
62
|
+
local_imps.append(full_imp_info)
|
|
63
|
+
return local_imps
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_all_pkg_paths(file_path):
|
|
67
|
+
pkg_paths = []
|
|
68
|
+
local_imps = return_local_imps(file_path)
|
|
69
|
+
for local_imp in local_imps:
|
|
70
|
+
pkg_path = local_imp.get('pkg_path')
|
|
71
|
+
if pkg_path:
|
|
72
|
+
pkg_paths.append(pkg_path)
|
|
73
|
+
return pkg_paths
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# --- NEW: Build dependency graph and detect circular imports ---
|
|
77
|
+
|
|
78
|
+
def build_graph(main_directory):
|
|
79
|
+
dirs, all_local_scripts = get_files_and_dirs(main_directory, allowd_exts='.py', files_only=True)
|
|
80
|
+
graph = defaultdict(set)
|
|
81
|
+
for file_path in all_local_scripts:
|
|
82
|
+
deps = get_all_pkg_paths(file_path)
|
|
83
|
+
for dep in deps:
|
|
84
|
+
if dep: # only valid files
|
|
85
|
+
graph[file_path].add(dep)
|
|
86
|
+
return graph
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def find_cycles(graph):
|
|
90
|
+
visited, stack, cycles = set(), [], []
|
|
91
|
+
|
|
92
|
+
def dfs(node, path):
|
|
93
|
+
visited.add(node)
|
|
94
|
+
path.append(node)
|
|
95
|
+
for dep in graph.get(node, []):
|
|
96
|
+
if dep not in visited:
|
|
97
|
+
dfs(dep, path.copy())
|
|
98
|
+
elif dep in path:
|
|
99
|
+
cycle_start = path.index(dep)
|
|
100
|
+
cycles.append(path[cycle_start:] + [dep])
|
|
101
|
+
|
|
102
|
+
for node in graph:
|
|
103
|
+
if node not in visited:
|
|
104
|
+
dfs(node, [])
|
|
105
|
+
return cycles
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
main_directory = "/home/flerb/Documents/pythonTools/modules/src/modules/abstract_utilities/src/abstract_utilities"
|
|
110
|
+
graph = build_graph(main_directory)
|
|
111
|
+
cycles = find_cycles(graph)
|
|
112
|
+
|
|
113
|
+
if not cycles:
|
|
114
|
+
print("✅ No circular imports found.")
|
|
115
|
+
else:
|
|
116
|
+
print("❌ Circular imports detected:")
|
|
117
|
+
for cycle in cycles:
|
|
118
|
+
print(" → ".join(cycle))
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
class SingletonMeta(type):
|
|
3
|
+
_instances = {}
|
|
4
|
+
def __call__(cls, *args, **kwargs):
|
|
5
|
+
if cls not in cls._instances:
|
|
6
|
+
cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
|
|
7
|
+
return cls._instances[cls]
|
|
8
|
+
|
|
9
|
+
def get_inputs(cls, *args, **kwargs):
|
|
10
|
+
"""
|
|
11
|
+
Dynamically construct a dataclass instance from args and kwargs,
|
|
12
|
+
filling missing values from defaults in the dataclass.
|
|
13
|
+
"""
|
|
14
|
+
fields = list(cls.__annotations__.keys())
|
|
15
|
+
values = {}
|
|
16
|
+
|
|
17
|
+
args = list(args)
|
|
18
|
+
for field in fields:
|
|
19
|
+
if field in kwargs:
|
|
20
|
+
values[field] = kwargs[field]
|
|
21
|
+
elif args:
|
|
22
|
+
values[field] = args.pop(0)
|
|
23
|
+
else:
|
|
24
|
+
values[field] = getattr(cls(), field) # default from dataclass
|
|
25
|
+
|
|
26
|
+
return cls(**values)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
from abstract_utilities import *
|
|
30
|
+
class SingletonMeta(type):
|
|
31
|
+
_instances = {}
|
|
32
|
+
def __call__(cls, *args, **kwargs):
|
|
33
|
+
if cls not in cls._instances:
|
|
34
|
+
cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
|
|
35
|
+
return cls._instances[cls]
|
|
36
|
+
|
|
37
|
+
def get_inputs(cls, *args, **kwargs):
|
|
38
|
+
"""
|
|
39
|
+
Dynamically construct a dataclass instance from args and kwargs,
|
|
40
|
+
filling missing values from defaults in the dataclass.
|
|
41
|
+
"""
|
|
42
|
+
fields = list(cls.__annotations__.keys())
|
|
43
|
+
values = {}
|
|
44
|
+
|
|
45
|
+
args = list(args)
|
|
46
|
+
for field in fields:
|
|
47
|
+
if field in kwargs:
|
|
48
|
+
values[field] = kwargs[field]
|
|
49
|
+
elif args:
|
|
50
|
+
values[field] = args.pop(0)
|
|
51
|
+
else:
|
|
52
|
+
values[field] = getattr(cls(), field) # default from dataclass
|
|
53
|
+
|
|
54
|
+
return cls(*args,**values)
|
|
55
|
+
def get_input_params(func):
|
|
56
|
+
sig = inspect.signature(func)
|
|
57
|
+
return sig.parameters
|
|
58
|
+
def get_args(func, *args,**kwargs):
|
|
59
|
+
parameters = get_input_params(func)
|
|
60
|
+
parameters = dict(parameters)
|
|
61
|
+
for key,value in parameters.items():
|
|
62
|
+
value = str(value)
|
|
63
|
+
if value.startswith('**'):
|
|
64
|
+
kwargs_key = key
|
|
65
|
+
elif value.startswith('*'):
|
|
66
|
+
args_key = key
|
|
67
|
+
kwargs_copy = kwargs.copy()
|
|
68
|
+
for k_key,k_value in kwargs.items():
|
|
69
|
+
if args_key == k_key and isinstance(k_value,list or tuple or set):
|
|
70
|
+
args = args | tuple(k_value)
|
|
71
|
+
del kwargs[k_key]
|
|
72
|
+
return args,kwargs
|
|
73
|
+
|
|
74
|
+
def prune_inputs(func, *args, **kwargs):
|
|
75
|
+
"""
|
|
76
|
+
Smart argument adapter:
|
|
77
|
+
- Detects if func accepts *args or **kwargs
|
|
78
|
+
- Builds new positional arguments from kwargs when appropriate
|
|
79
|
+
- Handles explicit {"args": [...]} convention
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
sig = inspect.signature(func)
|
|
83
|
+
params = sig.parameters
|
|
84
|
+
|
|
85
|
+
has_varargs = any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in params.values())
|
|
86
|
+
has_varkw = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in params.values())
|
|
87
|
+
|
|
88
|
+
new_args = list(args)
|
|
89
|
+
new_kwargs = dict(kwargs)
|
|
90
|
+
|
|
91
|
+
# -----------------------------------------------------------
|
|
92
|
+
# 1. If user provided explicit args: {"args": [...]}
|
|
93
|
+
# -----------------------------------------------------------
|
|
94
|
+
if "args" in new_kwargs:
|
|
95
|
+
explicit_args = new_kwargs.pop("args")
|
|
96
|
+
if isinstance(explicit_args, (list, tuple)):
|
|
97
|
+
new_args.extend(explicit_args)
|
|
98
|
+
else:
|
|
99
|
+
new_args.append(explicit_args)
|
|
100
|
+
|
|
101
|
+
# -----------------------------------------------------------
|
|
102
|
+
# 2. If function has *args, infer which kwargs belong there
|
|
103
|
+
# -----------------------------------------------------------
|
|
104
|
+
if has_varargs:
|
|
105
|
+
|
|
106
|
+
# Heuristic rules for upgrading kwargs to args:
|
|
107
|
+
# - if the function has NO named params, treat all scalar kwargs as positional
|
|
108
|
+
# - common param names like "file_path" also qualify
|
|
109
|
+
preferred_as_args = {"path", "file", "file_path", "filename", "value"}
|
|
110
|
+
|
|
111
|
+
positional_candidates = []
|
|
112
|
+
|
|
113
|
+
for k in list(new_kwargs.keys()):
|
|
114
|
+
v = new_kwargs[k]
|
|
115
|
+
|
|
116
|
+
# candidate rules:
|
|
117
|
+
if k in preferred_as_args:
|
|
118
|
+
positional_candidates.append(v)
|
|
119
|
+
del new_kwargs[k]
|
|
120
|
+
|
|
121
|
+
# scalars but not mappings/lists (optional)
|
|
122
|
+
elif isinstance(v, (str, int, float)) and len(positional_candidates) == 0:
|
|
123
|
+
positional_candidates.append(v)
|
|
124
|
+
del new_kwargs[k]
|
|
125
|
+
|
|
126
|
+
new_args.extend(positional_candidates)
|
|
127
|
+
|
|
128
|
+
# -----------------------------------------------------------
|
|
129
|
+
# 3. If function does NOT accept **kwargs → strip extras
|
|
130
|
+
# -----------------------------------------------------------
|
|
131
|
+
if not has_varkw:
|
|
132
|
+
allowed = {
|
|
133
|
+
name for name, p in params.items()
|
|
134
|
+
if p.kind in (
|
|
135
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
136
|
+
inspect.Parameter.KEYWORD_ONLY
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
new_kwargs = {k: v for k, v in new_kwargs.items() if k in allowed}
|
|
140
|
+
|
|
141
|
+
return tuple(new_args), new_kwargs
|
|
142
|
+
def run_pruned_func(func, *args, **kwargs):
|
|
143
|
+
args,kwargs = prune_inputs(func, *args, **kwargs)
|
|
144
|
+
return func(*args, **kwargs)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from .imports import *
|
|
2
|
+
|
|
3
|
+
def get_initial_caller() -> str:
|
|
4
|
+
"""
|
|
5
|
+
Return the TRUE original caller: the entrypoint script that launched the program.
|
|
6
|
+
"""
|
|
7
|
+
main_mod = sys.modules.get('__main__')
|
|
8
|
+
|
|
9
|
+
# interactive environments (REPL) may have no __file__
|
|
10
|
+
if not main_mod or not hasattr(main_mod, '__file__'):
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
return os.path.realpath(main_mod.__file__)
|
|
14
|
+
def get_initial_caller_dir() -> str:
|
|
15
|
+
"""
|
|
16
|
+
Return the directory of the TRUE original entrypoint script.
|
|
17
|
+
"""
|
|
18
|
+
caller = get_initial_caller()
|
|
19
|
+
return os.path.dirname(caller) if caller else None
|
|
20
|
+
|
|
21
|
+
def get_caller(i: Optional[int] = None) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Return the filename of the calling frame.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
i: Optional stack depth offset.
|
|
27
|
+
None = immediate caller (depth 1).
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Absolute path of the file for the stack frame.
|
|
31
|
+
"""
|
|
32
|
+
depth = 1 if i is None else int(i)
|
|
33
|
+
stack = inspect.stack()
|
|
34
|
+
if depth >= len(stack):
|
|
35
|
+
depth = len(stack) - 1
|
|
36
|
+
return stack[depth].filename
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_caller_path(i: Optional[int] = None) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Return the absolute path of the caller's file.
|
|
42
|
+
"""
|
|
43
|
+
depth = 1 if i is None else int(i)
|
|
44
|
+
file_path = get_caller(depth + 1)
|
|
45
|
+
return os.path.realpath(file_path)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_caller_dir(i: Optional[int] = None) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Return the absolute directory of the caller's file.
|
|
51
|
+
"""
|
|
52
|
+
depth = 1 if i is None else int(i)
|
|
53
|
+
abspath = get_caller_path(depth + 1)
|
|
54
|
+
return os.path.dirname(abspath)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_original_caller_dir(levels_up: int = None) -> Path:
|
|
58
|
+
"""
|
|
59
|
+
Return the directory of the *original* caller in the call stack.
|
|
60
|
+
|
|
61
|
+
levels_up:
|
|
62
|
+
- None → automatically goes to the bottom-most user-level caller.
|
|
63
|
+
- N → manually walk up N frames for custom behavior.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Path object pointing to caller's directory.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
stack = inspect.stack()
|
|
70
|
+
|
|
71
|
+
# If the user specifies an exact depth
|
|
72
|
+
if levels_up is not None:
|
|
73
|
+
target = min(levels_up + 1, len(stack) - 1)
|
|
74
|
+
frame = stack[target]
|
|
75
|
+
return Path(frame.filename).resolve().parent
|
|
76
|
+
|
|
77
|
+
# Otherwise, auto-detect the FIRST file that isn't inside site-packages or abstract_* utilities
|
|
78
|
+
for frameinfo in reversed(stack):
|
|
79
|
+
file_path = Path(frameinfo.filename).resolve()
|
|
80
|
+
|
|
81
|
+
# Skip internal interpreter/frame files
|
|
82
|
+
if "site-packages" in str(file_path):
|
|
83
|
+
continue
|
|
84
|
+
if "abstract_" in file_path.name:
|
|
85
|
+
continue
|
|
86
|
+
if file_path.name.startswith("<"):
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
return file_path.parent
|
|
90
|
+
|
|
91
|
+
# Fallback: last entry in the stack
|
|
92
|
+
return Path(stack[-1].filename).resolve().parent
|