abstract-utilities 0.2.2.540__py3-none-any.whl → 0.2.2.667__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 (66) hide show
  1. abstract_utilities/__init__.py +13 -4
  2. abstract_utilities/class_utils/abstract_classes.py +104 -34
  3. abstract_utilities/class_utils/caller_utils.py +57 -0
  4. abstract_utilities/class_utils/global_utils.py +35 -20
  5. abstract_utilities/class_utils/imports/imports.py +1 -1
  6. abstract_utilities/directory_utils/src/directory_utils.py +19 -1
  7. abstract_utilities/file_utils/imports/classes.py +59 -55
  8. abstract_utilities/file_utils/imports/imports.py +0 -4
  9. abstract_utilities/file_utils/imports/module_imports.py +1 -1
  10. abstract_utilities/file_utils/src/__init__.py +2 -3
  11. abstract_utilities/file_utils/src/file_filters/__init__.py +1 -0
  12. abstract_utilities/file_utils/src/file_filters/ensure_utils.py +490 -0
  13. abstract_utilities/file_utils/src/file_filters/filter_params.py +150 -0
  14. abstract_utilities/file_utils/src/file_filters/filter_utils.py +78 -0
  15. abstract_utilities/file_utils/src/file_filters/predicate_utils.py +44 -0
  16. abstract_utilities/file_utils/src/file_reader.py +0 -1
  17. abstract_utilities/file_utils/src/find_collect.py +10 -86
  18. abstract_utilities/file_utils/src/find_content.py +210 -0
  19. abstract_utilities/file_utils/src/initFunctionsGen.py +36 -23
  20. abstract_utilities/file_utils/src/initFunctionsGens.py +280 -0
  21. abstract_utilities/file_utils/src/reader_utils/__init__.py +4 -0
  22. abstract_utilities/file_utils/src/reader_utils/directory_reader.py +53 -0
  23. abstract_utilities/file_utils/src/reader_utils/file_reader.py +543 -0
  24. abstract_utilities/file_utils/src/reader_utils/file_readers.py +376 -0
  25. abstract_utilities/file_utils/src/reader_utils/imports.py +18 -0
  26. abstract_utilities/file_utils/src/reader_utils/pdf_utils.py +300 -0
  27. abstract_utilities/import_utils/circular_import_finder.py +222 -0
  28. abstract_utilities/import_utils/circular_import_finder2.py +118 -0
  29. abstract_utilities/import_utils/imports/__init__.py +1 -1
  30. abstract_utilities/import_utils/imports/init_imports.py +3 -0
  31. abstract_utilities/import_utils/imports/module_imports.py +4 -1
  32. abstract_utilities/import_utils/imports/utils.py +1 -1
  33. abstract_utilities/import_utils/src/__init__.py +1 -0
  34. abstract_utilities/import_utils/src/clean_imports.py +156 -25
  35. abstract_utilities/import_utils/src/dot_utils.py +11 -0
  36. abstract_utilities/import_utils/src/extract_utils.py +4 -0
  37. abstract_utilities/import_utils/src/import_functions.py +66 -2
  38. abstract_utilities/import_utils/src/import_utils.py +39 -0
  39. abstract_utilities/import_utils/src/layze_import_utils/__init__.py +2 -0
  40. abstract_utilities/import_utils/src/layze_import_utils/lazy_utils.py +41 -0
  41. abstract_utilities/import_utils/src/layze_import_utils/nullProxy.py +32 -0
  42. abstract_utilities/import_utils/src/nullProxy.py +30 -0
  43. abstract_utilities/import_utils/src/pkg_utils.py +58 -4
  44. abstract_utilities/import_utils/src/sysroot_utils.py +56 -1
  45. abstract_utilities/imports.py +3 -2
  46. abstract_utilities/json_utils/json_utils.py +11 -3
  47. abstract_utilities/log_utils/log_file.py +73 -24
  48. abstract_utilities/parse_utils/parse_utils.py +23 -0
  49. abstract_utilities/path_utils/imports/module_imports.py +1 -1
  50. abstract_utilities/path_utils/path_utils.py +32 -35
  51. abstract_utilities/read_write_utils/imports/imports.py +1 -1
  52. abstract_utilities/read_write_utils/read_write_utils.py +102 -32
  53. abstract_utilities/safe_utils/safe_utils.py +30 -0
  54. abstract_utilities/type_utils/__init__.py +5 -1
  55. abstract_utilities/type_utils/get_type.py +116 -0
  56. abstract_utilities/type_utils/imports/__init__.py +1 -0
  57. abstract_utilities/type_utils/imports/constants.py +134 -0
  58. abstract_utilities/type_utils/imports/module_imports.py +25 -1
  59. abstract_utilities/type_utils/is_type.py +455 -0
  60. abstract_utilities/type_utils/make_type.py +126 -0
  61. abstract_utilities/type_utils/mime_types.py +68 -0
  62. abstract_utilities/type_utils/type_utils.py +0 -877
  63. {abstract_utilities-0.2.2.540.dist-info → abstract_utilities-0.2.2.667.dist-info}/METADATA +1 -1
  64. {abstract_utilities-0.2.2.540.dist-info → abstract_utilities-0.2.2.667.dist-info}/RECORD +66 -41
  65. {abstract_utilities-0.2.2.540.dist-info → abstract_utilities-0.2.2.667.dist-info}/WHEEL +0 -0
  66. {abstract_utilities-0.2.2.540.dist-info → abstract_utilities-0.2.2.667.dist-info}/top_level.txt +0 -0
@@ -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))
@@ -1,4 +1,4 @@
1
- from .imports import *
1
+ from .init_imports import *
2
2
  from .module_imports import *
3
3
  from .constants import *
4
4
  from .utils import *
@@ -0,0 +1,3 @@
1
+ from ...imports import *
2
+
3
+
@@ -1,6 +1,9 @@
1
1
  from ...read_write_utils import read_from_file,write_to_file,get_text_or_read
2
2
  from ...string_utils import eatAll,eatInner,eatElse,clean_line
3
- from ...class_utils import get_caller_path
3
+ from ...class_utils import if_none_change,if_none_default,get_true_globals,get_initial_caller_dir,get_caller_path,get_caller_dir,if_none_default
4
4
  from ...list_utils import make_list
5
5
  from ...path_utils import get_file_parts
6
6
  from ...type_utils import is_number,make_list
7
+ from ...file_utils import collect_filepaths,collect_globs
8
+ from ...directory_utils import get_shortest_path,get_common_root
9
+ from ...log_utils import get_logFile
@@ -1,4 +1,4 @@
1
- import os
1
+ from .init_imports import *
2
2
  from .constants import *
3
3
 
4
4
  def is_line_import(line):
@@ -5,3 +5,4 @@ from .import_utils import *
5
5
  from .import_functions import *
6
6
  from .pkg_utils import *
7
7
  from .sysroot_utils import *
8
+ from .layze_import_utils import *
@@ -1,12 +1,64 @@
1
1
  from ..imports import *
2
2
  from .pkg_utils import *
3
-
3
+ from ...safe_utils import *
4
+ def is_local_import(line):
5
+ print(line)
6
+ imports_from_import_pkg = clean_imports(line)
7
+ input(imports_from_import_pkg)
8
+ def try_is_file(file_path):
9
+ try:
10
+ return os.path.isfile(file_path)
11
+ except:
12
+ return False
13
+ def try_is_dir(file_path):
14
+ try:
15
+ return os.path.isdir(file_path)
16
+ except:
17
+ return False
18
+ def try_join(*args):
19
+ try:
20
+ return safe_join(*args)
21
+ except:
22
+ return False
23
+ def get_pkg_or_init(pkg_path):
24
+ if pkg_path:
25
+ if try_is_file(pkg_path):
26
+ return pkg_path
27
+ pkg_py_path = f"{pkg_path}.py"
28
+ if try_is_file(pkg_py_path):
29
+ return pkg_py_path
30
+ pkg_init_path = try_join(pkg_path,'__init__.py')
31
+ if try_is_dir(pkg_path):
32
+ if os.path.isfile(pkg_init_path):
33
+ return pkg_init_path
34
+ def get_text_and_file_and_js(text=None,file_path=None,import_pkg_js=None):
35
+ inputs = {"text":text,"file_path":file_path,"import_pkg_js":import_pkg_js}
36
+ for key,value in inputs.items():
37
+ if value:
38
+ if isinstance(value,str):
39
+ _file_path = get_pkg_or_init(file_path)
40
+ if _file_path:
41
+ file_path=_file_path
42
+ if key == "text" or text == None:
43
+ text=read_from_file(file_path)
44
+ if isinstance(value,dict):
45
+ if key in ["text","file_path"]:
46
+ if key == "text":
47
+ text = None
48
+ if key == "file_path":
49
+ file_path = None
50
+ import_pkg_js=value
51
+ import_pkg_js = ensure_import_pkg_js(import_pkg_js,file_path=file_path)
52
+ return text,file_path,import_pkg_js
4
53
  def get_text_or_read(text=None,file_path=None):
5
- text = text or ''
6
- imports_js = {}
7
- if not text and file_path and os.path.isfile(file_path):
54
+ file_path = get_pkg_or_init(file_path)
55
+ if not text and file_path:
8
56
  text=read_from_file(file_path)
9
- return text
57
+ if text and not file_path:
58
+ file_path=get_pkg_or_init(text)
59
+ if file_path:
60
+ text = None
61
+ return text,file_path
10
62
  def is_line_import(line):
11
63
  if line and (line.startswith(FROM_TAG) or line.startswith(IMPORT_TAG)):
12
64
  return True
@@ -25,19 +77,18 @@ def is_from_line_group(line):
25
77
  return False
26
78
 
27
79
  def get_all_imports(text=None,file_path=None,import_pkg_js=None):
28
- if text and os.path.isfile(text):
29
- file_path = text
30
- text = read_from_file(text)
31
- text = get_text_or_read(text=text,file_path=file_path)
80
+ text,file_path = get_text_or_read(text=text,file_path=file_path)
32
81
  lines = text.split('\n')
33
82
  cleaned_import_list=[]
34
83
  nu_lines = []
35
84
  is_from_group = False
36
85
  import_pkg_js = ensure_import_pkg_js(import_pkg_js,file_path=file_path)
37
86
  for line in lines:
87
+
38
88
  if line.startswith(IMPORT_TAG) and ' from ' not in line:
89
+
39
90
  cleaned_import_list = get_cleaned_import_list(line)
40
- import_pkg_js = add_imports_to_import_pkg_js("import",cleaned_import_list,import_pkg_js=import_pkg_js)
91
+ import_pkg_js = add_imports_to_import_pkg_js("import",cleaned_import_list,import_pkg_js=import_pkg_js,file_path=file_path)
41
92
  else:
42
93
  if is_from_group:
43
94
  import_pkg=is_from_group
@@ -46,18 +97,16 @@ def get_all_imports(text=None,file_path=None,import_pkg_js=None):
46
97
  is_from_group=False
47
98
  line=line[:-1]
48
99
  imports_from_import_pkg = clean_imports(line)
49
- import_pkg_js = add_imports_to_import_pkg_js(import_pkg,imports_from_import_pkg,import_pkg_js=import_pkg_js)
100
+ import_pkg_js = add_imports_to_import_pkg_js(import_pkg,imports_from_import_pkg,import_pkg_js=import_pkg_js,file_path=file_path)
50
101
 
51
102
  else:
52
- import_pkg_js=update_import_pkg_js(line,import_pkg_js=import_pkg_js)
103
+ import_pkg_js=update_import_pkg_js(line,import_pkg_js=import_pkg_js,file_path=file_path)
53
104
  if is_from_line_group(line) and is_from_group == False:
54
105
  is_from_group=get_import_pkg(line)
55
106
  return import_pkg_js
56
107
 
57
- def clean_imports(text=None,file_path=None,import_pkg_js=None,fill_nulines=False):
58
- if text and os.path.isfile(text):
59
- file_path = text
60
- text = read_from_file(text)
108
+ def get_clean_imports(text=None,file_path=None,import_pkg_js=None,fill_nulines=False):
109
+ text,file_path,_ = get_text_and_file_and_js(text=text,file_path=file_path,import_pkg_js=import_pkg_js)
61
110
  if not import_pkg_js:
62
111
  import_pkg_js = get_all_imports(text=text,file_path=file_path)
63
112
  import_pkg_js = ensure_import_pkg_js(import_pkg_js,file_path=file_path)
@@ -87,11 +136,11 @@ def clean_imports(text=None,file_path=None,import_pkg_js=None,fill_nulines=False
87
136
  nu_lines[line] += imports
88
137
  return import_pkg_js
89
138
  def clean_all_imports(text=None,file_path=None,import_pkg_js=None,fill_nulines=False):
90
- clean_imports(text=text,file_path=file_path,import_pkg_js=import_pkg_js,fill_nulines=import_pkg_js)
91
- import_pkg_js["context"]["nulines"]=nu_lines
139
+ import_pkg_js = get_clean_imports(text=text,file_path=file_path,import_pkg_js=import_pkg_js,fill_nulines=import_pkg_js)
140
+ import_pkg_js["context"]["nulines"]=import_pkg_js["context"]["nulines"]
92
141
  return import_pkg_js
93
142
  def get_clean_import_string(import_pkg_js,fill_nulines=False,get_locals=False):
94
- import_pkg_js = clean_imports(import_pkg_js=import_pkg_js,fill_nulines=fill_nulines)
143
+ import_pkg_js = get_clean_imports(import_pkg_js=import_pkg_js,fill_nulines=fill_nulines)
95
144
  import_ls = []
96
145
  for key,values in import_pkg_js.items():
97
146
  if key not in ['context','nulines']:
@@ -130,18 +179,100 @@ def get_dot_fro_lines(lines,file_path,all_imps):
130
179
  all_imps.append(line)
131
180
  return all_imps
132
181
  def get_all_real_imps(text=None,file_path=None,all_imps=None):
133
- if text and os.path.isfile(text):
134
- file_path = text
135
- text = read_from_file(text)
182
+
136
183
  all_imps = all_imps or []
137
- contents = get_text_or_read(text=text,file_path=file_path)
138
- lines = contents.split('\n')
184
+ text,file_path = get_text_or_read(text=text,file_path=file_path)
185
+ lines = text.split('\n')
139
186
  all_imps = get_dot_fro_lines(lines,file_path,all_imps)
140
187
  return '\n'.join(all_imps)
141
188
  def save_cleaned_imports(text=None,file_path=None,write=False,import_pkg_js=None):
142
- import_pkg_js=get_all_imports(text=text,file_path=file_path,import_pkg_js=import_pkg_js)
189
+ text,file_path,import_pkg_js = get_text_and_file_and_js(text=text,file_path=file_path,import_pkg_js=import_pkg_js)
143
190
  import_pkg_js = clean_all_imports(text=text,file_path=file_path,import_pkg_js=import_pkg_js)
144
191
  contents = '\n'.join(import_pkg_js["context"]["nulines"])
145
192
  if file_path and write:
146
193
  write_to_file(contents=contents,file_path=file_path)
147
194
  return contents
195
+ def convert_to_sysroot_relative(import_pkg, file_path, sysroot):
196
+ """
197
+ Convert an absolute package import into a dotted relative import based on
198
+ the file's depth inside sysroot.
199
+ """
200
+
201
+ if not sysroot:
202
+ return import_pkg # no conversion
203
+
204
+ file_path = os.path.abspath(file_path)
205
+ sysroot = os.path.abspath(sysroot)
206
+
207
+ # Ignore imports outside sysroot
208
+ file_dir = os.path.dirname(file_path)
209
+ if not file_dir.startswith(sysroot):
210
+ return import_pkg
211
+
212
+ # Compute how many directories deep the file is
213
+ rel = os.path.relpath(file_dir, sysroot)
214
+ depth = 0 if rel == "." else len(rel.split(os.sep))
215
+
216
+ # Depth N means N dots (i.e. N relative levels)
217
+ dots = "." * depth
218
+
219
+ return f"{dots}{import_pkg}"
220
+
221
+ import os
222
+
223
+ def rewrite_import_with_sysroot(line, file_path, sysroot):
224
+ """
225
+ Rewrite imports like:
226
+ from imports.constants import *
227
+ into:
228
+ from <relative_path>.imports.constants import *
229
+ Where <relative_path> is computed relative to sysroot.
230
+ """
231
+
232
+ line = line.rstrip()
233
+ if not line.startswith("from "):
234
+ return line
235
+
236
+ # Split import structure
237
+ try:
238
+ after_from = line[len("from "):]
239
+ pkg, after_import = after_from.split(" import ", 1)
240
+ except ValueError:
241
+ return line # Not a normal from X import Y
242
+
243
+ # Absolute paths
244
+ file_dir = os.path.dirname(os.path.abspath(file_path))
245
+ sysroot = os.path.abspath(sysroot)
246
+
247
+ # Compute relative path
248
+ relpath = os.path.relpath(file_dir, sysroot)
249
+
250
+ # Turn filesystem path into dotted python path
251
+ if relpath == ".":
252
+ dotted = ""
253
+ else:
254
+ dotted = ".".join(part for part in relpath.split(os.sep) if part)
255
+
256
+ # Import path you want to append the old import to
257
+ new_pkg = f"{dotted}.{pkg}".lstrip('.')
258
+
259
+ # Build final rewritten import
260
+ return f"from {new_pkg} import {after_import}"
261
+
262
+ def trace_all_imports(file_path, sysroot=None):
263
+ import_pkg_js = {}
264
+ files = collect_filepaths(file_path, allowed_exts='.py', add=True)
265
+
266
+ for file in files:
267
+ text = get_all_real_imps(file_path=file)
268
+ lines = text.split("\n")
269
+
270
+ if sysroot:
271
+ new_lines = []
272
+ for line in lines:
273
+ new_lines.append(rewrite_import_with_sysroot(line, file, sysroot))
274
+ text = "\n".join(new_lines)
275
+
276
+ import_pkg_js = get_all_imports(text=text, file_path=file, import_pkg_js=import_pkg_js)
277
+
278
+ return get_clean_import_string(import_pkg_js)
@@ -1,4 +1,15 @@
1
1
  from ..imports import *
2
+ def find_top_package_dir(p: Path) -> Path | None:
3
+ p = p.resolve()
4
+ if p.is_file():
5
+ p = p.parent
6
+ top = None
7
+ while (p / "__init__.py").exists():
8
+ top = p
9
+ if p.parent == p:
10
+ break
11
+ p = p.parent
12
+ return top
2
13
  def get_Path(path):
3
14
  if isinstance(path,str):
4
15
  path = Path(str(path))
@@ -40,3 +40,7 @@ def extract_class(path: str):
40
40
  if m:
41
41
  funcs.append(m.group(1))
42
42
  return funcs
43
+ def get_all_py_file_paths(directory,*args,**kwargs):
44
+ globs = collect_globs(directory,*args,allowed_exts='.py',**kwargs)
45
+ globs = [glo for glo in globs.get('files') if glo]
46
+ return globs