ArchLens 0.2__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.
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "type": "object",
4
+ "required": ["name", "rootFolder", "views"],
5
+ "properties": {
6
+ "name": {
7
+ "type": "string",
8
+ "description": "The name of the project",
9
+ "minLength": 1
10
+ },
11
+ "rootFolder": {
12
+ "type": "string",
13
+ "description": "Point to the root package of the project",
14
+ "minLength": 1
15
+ },
16
+ "github": {
17
+ "type": "object",
18
+ "description": "Information about the GitHub repository for the project",
19
+ "required": ["url", "branch"],
20
+ "properties": {
21
+ "url": {
22
+ "type": "string",
23
+ "description": "The URL of the GitHub repository for the project"
24
+ },
25
+ "branch": {
26
+ "type": "string",
27
+ "description": "The name of the branch to use for the GitHub repository"
28
+ }
29
+ }
30
+ },
31
+ "SaveLocation": {
32
+ "type": "string",
33
+ "description": "The folder to which the diagrams should be saved in. If the folder does not exists it will be created",
34
+ "default": "./diagrams/"
35
+ },
36
+ "showDependencyCount": {
37
+ "type": "boolean",
38
+ "description": "If to showcase the number of dependencies between packages"
39
+ },
40
+ "packageColor": {
41
+ "type": "string",
42
+ "enum": ["#GoldenRod", "#Azure", ""]
43
+ },
44
+ "views": {
45
+ "type": "object",
46
+ "description": "Defines the views available",
47
+ "patternProperties": {
48
+ ".*": {
49
+ "description": "The name of the view",
50
+ "type": "object",
51
+ "properties": {
52
+ "packages": {
53
+ "description": "The packages to include in the view. Leave empty to include all packages",
54
+ "type": "array",
55
+ "items": {
56
+ "oneOf": [
57
+ {
58
+ "type": "string",
59
+ "pattern": "^[A-Za-z0-9._-]+$"
60
+ },
61
+ {
62
+ "type": "object",
63
+ "properties": {
64
+ "path": {
65
+ "type": "string",
66
+ "pattern": "^[A-Za-z0-9._\\-]+|[*]$"
67
+ },
68
+ "depth": {
69
+ "type": "integer"
70
+ }
71
+ },
72
+ "required": ["path", "depth"]
73
+ }
74
+ ]
75
+ }
76
+ },
77
+ "ignorePackages": {
78
+ "type": "array",
79
+ "items": {
80
+ "type": "string",
81
+ "pattern": "^[A-Za-z0-9._\\-\\/*]+$"
82
+ },
83
+ "description": "Defines the packages to ignore"
84
+ },
85
+ "usePackagePathAsLabel": {
86
+ "type": "boolean",
87
+ "description": "If true, paths of each package will be displayed, if false only package name will be displayed"
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/archlens/ArchLens/master/src/config.schema.json",
3
+ "name": "",
4
+ "rootFolder": "",
5
+ "github": {
6
+ "url": "",
7
+ "branch": "main"
8
+ },
9
+ "saveLocation": "./diagrams/",
10
+ "views": {
11
+ "completeView": {
12
+ "packages": [],
13
+ "ignorePackages": []
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: ArchLens
3
+ Version: 0.2
4
+ Summary: Designed for visualizing package dependencies and highlighting differences between branches in GitHub pull requests. It offers customization options to tailor package views.
5
+ Home-page: https://github.com/archlens/ArchLens
6
+ Author: The ArchLens Team
7
+ Author-email: mlun@itu.dk
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Requires-Dist: plantuml
11
+ Requires-Dist: typer
12
+ Requires-Dist: astroid
13
+ Requires-Dist: six
14
+ Requires-Dist: requests
15
+ Requires-Dist: jsonschema
16
+ Requires-Dist: gitpython
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: home-page
22
+ Dynamic: requires-dist
23
+ Dynamic: summary
24
+
25
+ This is the long description
@@ -0,0 +1,31 @@
1
+ archlens-0.2.data/data/config.schema.json,sha256=WexuktJolnluIvoOE4YGWoxY4rRlU9lr3f0L2lKELWA,2901
2
+ archlens-0.2.data/data/config.template.json,sha256=mdJse5TRDy_V0KpN9UFaFRFo23ZbOQ32Y4hwJgDJj6c,318
3
+ src/__init__.py,sha256=-d7TWFKkXpRRhH9rz7n-2n623B_HqgfMd1rpbRfpXCM,64
4
+ src/cli_interface.py,sha256=NtjNKVw88HTwLnEbJCP893Ta-yOzHcQkUWzKdeYvbHA,5084
5
+ src/config.schema.json,sha256=WexuktJolnluIvoOE4YGWoxY4rRlU9lr3f0L2lKELWA,2901
6
+ src/config.template.json,sha256=mdJse5TRDy_V0KpN9UFaFRFo23ZbOQ32Y4hwJgDJj6c,318
7
+ src/main.py,sha256=YbJCHt-qZHNxtl9z1QYOesw8b8OLkQKsML2QmZTHNVA,30
8
+ src/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ src/core/bt_file.py,sha256=ugNwjTiiisqaXr0iQJ4IG9s2W3mpuv_wPT23eOU25Jg,2768
10
+ src/core/bt_graph.py,sha256=TfDEbMcDFjsBe1A2_g610FMKG7zM6Rf6i1U0D3SAiK0,3859
11
+ src/core/bt_module.py,sha256=cGtKiJxazojyjmPEoTHtk-zFSE_12lY_zLjXh3Tad3s,3147
12
+ src/git_integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ src/git_integration/fetch_git.py,sha256=yqDWi_fk-zxkdApmE3SKEmx0uXcBux1Fw3K0Mhh8w6g,244
14
+ src/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ src/providers/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ src/providers/json/json_render.py,sha256=iCPtE8thUFcOzDZNyET0VvU6E4uCcT-D9HmoWx3rono,1465
17
+ src/providers/plantuml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ src/providers/plantuml/pu_render.py,sha256=WEozrq9AWUr4Cg7LqhyVoT04Vg4Z8QzOT_5nTYSEUSM,1820
19
+ src/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ src/utils/config_manager_singleton.py,sha256=n3Vp-JSaoXpD5ukBUgKxXdmTWIlapth8gZ53C0WEYsY,424
21
+ src/utils/functions.py,sha256=TbLO4o9qJ-iaioN1S0HDE70AAFm7SxQaHjGgd-nNrSo,1520
22
+ src/utils/path_manager_singleton.py,sha256=VhKzS82tRZ6QNJTq6tQKJmUSHyr29yrGmxZXdSmpJn0,1850
23
+ src/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ src/views/utils.py,sha256=BJi7RcURuA3oOLrl8g6DdROVIjVtrJLoq0ZuYydQU_4,329
25
+ src/views/view_entities.py,sha256=-LbDYMyaJr-QMPQNR17TiVd3kzbc6PbGlfn7N7tZsOg,7918
26
+ src/views/view_manager.py,sha256=-mwxDkYOMb23UiqcFNyB1dR0h_e61dsY8_sRf5NQIKI,10834
27
+ archlens-0.2.dist-info/METADATA,sha256=0ZBD6oQNweOw0x28StGsNWbAnNpzj6C-kDAT2h3ElKc,764
28
+ archlens-0.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
29
+ archlens-0.2.dist-info/entry_points.txt,sha256=eOn74xvh44lrsjdvJ89_1uzx7XB0DeAaMwYnWEjI748,52
30
+ archlens-0.2.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
31
+ archlens-0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ archlens = src.cli_interface:main
@@ -0,0 +1 @@
1
+ src
src/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ import sys
2
+ import os
3
+
4
+ sys.path.insert(0, os.path.abspath(".."))
src/cli_interface.py ADDED
@@ -0,0 +1,176 @@
1
+ import typer
2
+ import json
3
+ import os
4
+ import requests
5
+ import jsonschema
6
+ import tempfile
7
+ import shutil
8
+ from pathlib import Path
9
+ from src.providers.json.json_render import save_json, save_json_diff
10
+ from src.providers.plantuml.pu_render import save_plant_uml, save_plant_uml_diff
11
+ from src.utils.path_manager_singleton import PathManagerSingleton
12
+
13
+ # from src.utils.functions import verify_config_options
14
+ from src.utils.config_manager_singleton import ConfigManagerSingleton
15
+
16
+ from src.views.view_manager import render_views, render_diff_views
17
+
18
+ from src.core.bt_graph import BTGraph
19
+
20
+ from src.git_integration.fetch_git import fetch_git_repo
21
+
22
+ from astroid.manager import AstroidManager
23
+
24
+ import astroid
25
+
26
+ astroid.MANAGER = None
27
+
28
+ app = typer.Typer(add_completion=True)
29
+
30
+
31
+ @app.command()
32
+ def render(config_path: str = "archlens.json"):
33
+ config = read_config_file(config_path)
34
+
35
+ mt_path_manager = PathManagerSingleton()
36
+ mt_path_manager.setup(config)
37
+
38
+ am = _create_astroid()
39
+ g = BTGraph(am)
40
+ g.build_graph(config)
41
+
42
+ render_views(g, config, save_plant_uml)
43
+
44
+
45
+ @app.command()
46
+ def render_json(config_path: str = "archlens.json"):
47
+ config = read_config_file(config_path)
48
+
49
+ mt_path_manager = PathManagerSingleton()
50
+ mt_path_manager.setup(config)
51
+
52
+ am = _create_astroid()
53
+ g = BTGraph(am)
54
+ g.build_graph(config)
55
+
56
+ render_views(g, config, save_json)
57
+
58
+
59
+ def _create_astroid():
60
+ am = AstroidManager()
61
+ am.brain["astroid_cache"] = {}
62
+ return am
63
+
64
+
65
+ @app.command()
66
+ def render_diff(config_path: str = "archlens.json"):
67
+ with tempfile.TemporaryDirectory() as tmp_dir:
68
+ print("Created temporary directory:", tmp_dir)
69
+ config = read_config_file(config_path)
70
+
71
+ fetch_git_repo(tmp_dir, config["github"]["url"], config["github"]["branch"])
72
+
73
+ shutil.copyfile(config_path, os.path.join(tmp_dir, "archlens.json"))
74
+
75
+ config_git = read_config_file(os.path.join(tmp_dir, "archlens.json"))
76
+
77
+ path_manager = PathManagerSingleton()
78
+ path_manager.setup(config, config_git)
79
+
80
+ local_am = _create_astroid()
81
+ local_graph = BTGraph(local_am)
82
+ local_graph.build_graph(config)
83
+ # verify_config_options(config, g)
84
+
85
+ remote_am = _create_astroid()
86
+ remote_graph = BTGraph(remote_am)
87
+ remote_graph.build_graph(config_git)
88
+ # verify_config_options(config_git, g_git)
89
+
90
+ render_diff_views(local_graph, remote_graph, config, save_plant_uml_diff)
91
+
92
+
93
+ @app.command()
94
+ def render_diff_json(config_path: str = "archlens.json"):
95
+ with tempfile.TemporaryDirectory() as tmp_dir:
96
+ print("Created temporary directory:", tmp_dir)
97
+ config = read_config_file(config_path)
98
+
99
+ fetch_git_repo(tmp_dir, config["github"]["url"], config["github"]["branch"])
100
+
101
+ shutil.copyfile(config_path, os.path.join(tmp_dir, "archlens.json"))
102
+
103
+ config_git = read_config_file(os.path.join(tmp_dir, "archlens.json"))
104
+
105
+ path_manager = PathManagerSingleton()
106
+ path_manager.setup(config, config_git)
107
+
108
+ local_am = _create_astroid()
109
+ local_graph = BTGraph(local_am)
110
+ local_graph.build_graph(config)
111
+ # verify_config_options(config, g)
112
+
113
+ remote_am = _create_astroid()
114
+ remote_graph = BTGraph(remote_am)
115
+ remote_graph.build_graph(config_git)
116
+ # verify_config_options(config_git, g_git)
117
+
118
+ render_diff_views(local_graph, remote_graph, config, save_json_diff)
119
+
120
+
121
+ @app.command()
122
+ def init(config_path="./archlens.json"):
123
+ os.makedirs(os.path.dirname(config_path), exist_ok=True)
124
+ template_path = os.path.join(os.path.dirname(__file__), "config.template.json")
125
+ schema = None
126
+ with open(template_path, "r") as f:
127
+ schema = json.load(f)
128
+
129
+ schema["name"] = os.path.basename(os.getcwd())
130
+ with open(config_path, "w") as outfile:
131
+ json.dump(schema, outfile, indent=4)
132
+
133
+
134
+ @app.command()
135
+ def create_action():
136
+ action_url = "https://raw.githubusercontent.com/archlens/ArchLens/master/.github/workflows/render-diff-on-pr.yml"
137
+ action_path = Path(".github/workflows/render-diff-on-pr.yml")
138
+ typer.secho(f"Creating the action at {action_path}", fg="green")
139
+ action_path.parent.mkdir(parents=True, exist_ok=True)
140
+ action = requests.get(action_url).text
141
+
142
+ with open(action_path, "w") as f:
143
+ f.write(action)
144
+
145
+
146
+ def read_config_file(config_path):
147
+ config = None
148
+ with open(config_path, "r") as f:
149
+ config = json.load(f)
150
+
151
+ config_schema = None
152
+ schema_path = os.path.join(os.path.dirname(__file__), "config.schema.json")
153
+ with open(schema_path) as fp:
154
+ config_schema = json.load(fp)
155
+
156
+ if not os.getenv("MT_DEBUG"):
157
+ jsonschema.validate(instance=config, schema=config_schema)
158
+
159
+ config["_config_path"] = os.path.dirname(os.path.abspath(config_path))
160
+
161
+ config["saveLocation"] = os.path.normpath(
162
+ os.path.join(config["_config_path"], config["saveLocation"])
163
+ )
164
+
165
+ config_manager = ConfigManagerSingleton()
166
+ config_manager.setup(config)
167
+
168
+ return config
169
+
170
+
171
+ def main():
172
+ app()
173
+
174
+
175
+ if __name__ == "__main__":
176
+ main()
src/config.schema.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "type": "object",
4
+ "required": ["name", "rootFolder", "views"],
5
+ "properties": {
6
+ "name": {
7
+ "type": "string",
8
+ "description": "The name of the project",
9
+ "minLength": 1
10
+ },
11
+ "rootFolder": {
12
+ "type": "string",
13
+ "description": "Point to the root package of the project",
14
+ "minLength": 1
15
+ },
16
+ "github": {
17
+ "type": "object",
18
+ "description": "Information about the GitHub repository for the project",
19
+ "required": ["url", "branch"],
20
+ "properties": {
21
+ "url": {
22
+ "type": "string",
23
+ "description": "The URL of the GitHub repository for the project"
24
+ },
25
+ "branch": {
26
+ "type": "string",
27
+ "description": "The name of the branch to use for the GitHub repository"
28
+ }
29
+ }
30
+ },
31
+ "SaveLocation": {
32
+ "type": "string",
33
+ "description": "The folder to which the diagrams should be saved in. If the folder does not exists it will be created",
34
+ "default": "./diagrams/"
35
+ },
36
+ "showDependencyCount": {
37
+ "type": "boolean",
38
+ "description": "If to showcase the number of dependencies between packages"
39
+ },
40
+ "packageColor": {
41
+ "type": "string",
42
+ "enum": ["#GoldenRod", "#Azure", ""]
43
+ },
44
+ "views": {
45
+ "type": "object",
46
+ "description": "Defines the views available",
47
+ "patternProperties": {
48
+ ".*": {
49
+ "description": "The name of the view",
50
+ "type": "object",
51
+ "properties": {
52
+ "packages": {
53
+ "description": "The packages to include in the view. Leave empty to include all packages",
54
+ "type": "array",
55
+ "items": {
56
+ "oneOf": [
57
+ {
58
+ "type": "string",
59
+ "pattern": "^[A-Za-z0-9._-]+$"
60
+ },
61
+ {
62
+ "type": "object",
63
+ "properties": {
64
+ "path": {
65
+ "type": "string",
66
+ "pattern": "^[A-Za-z0-9._\\-]+|[*]$"
67
+ },
68
+ "depth": {
69
+ "type": "integer"
70
+ }
71
+ },
72
+ "required": ["path", "depth"]
73
+ }
74
+ ]
75
+ }
76
+ },
77
+ "ignorePackages": {
78
+ "type": "array",
79
+ "items": {
80
+ "type": "string",
81
+ "pattern": "^[A-Za-z0-9._\\-\\/*]+$"
82
+ },
83
+ "description": "Defines the packages to ignore"
84
+ },
85
+ "usePackagePathAsLabel": {
86
+ "type": "boolean",
87
+ "description": "If true, paths of each package will be displayed, if false only package name will be displayed"
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/archlens/ArchLens/master/src/config.schema.json",
3
+ "name": "",
4
+ "rootFolder": "",
5
+ "github": {
6
+ "url": "",
7
+ "branch": "main"
8
+ },
9
+ "saveLocation": "./diagrams/",
10
+ "views": {
11
+ "completeView": {
12
+ "packages": [],
13
+ "ignorePackages": []
14
+ }
15
+ }
16
+ }
src/core/__init__.py ADDED
File without changes
src/core/bt_file.py ADDED
@@ -0,0 +1,95 @@
1
+ import astroid
2
+ from astroid.manager import AstroidManager
3
+
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from src.core.bt_module import BTModule
8
+
9
+
10
+ class BTFile:
11
+ label: str = ""
12
+ edge_to: list["BTFile"] = None
13
+ ast = None
14
+ module: "BTModule" = None
15
+ am: AstroidManager
16
+
17
+ def __init__(self, label: str, module, am: AstroidManager, code_path: str = None):
18
+ self.label = label
19
+ self.ast = None
20
+ self.am = am
21
+
22
+ if code_path is not None:
23
+ self.ast: astroid.Module = self.am.ast_from_module_name(code_path)
24
+
25
+ self.edge_to = []
26
+ self.module = module
27
+
28
+ @property
29
+ def file(self):
30
+ if self.ast:
31
+ return self.ast.file
32
+ return ""
33
+
34
+ @property
35
+ def uid(self):
36
+ if self.ast:
37
+ return self.ast.file
38
+ else:
39
+ return self.label
40
+
41
+ @property
42
+ def module_path(self) -> str:
43
+ if not self.ast:
44
+ return None
45
+ return "/".join(self.file.split("/")[:-1])
46
+
47
+ def __rshift__(self, other):
48
+ if isinstance(other, list):
49
+ existing_edges = set(
50
+ [edge.file for edge in self.edge_to if edge.file != ""]
51
+ )
52
+ new_node_list = filter(lambda e: e.file not in existing_edges, other)
53
+ self.edge_to.extend([node for node in new_node_list])
54
+ else:
55
+ edges = set([edge.file for edge in self.edge_to])
56
+ if other.file in edges:
57
+ return
58
+
59
+ self.edge_to.append(other)
60
+
61
+
62
+ def get_imported_modules(
63
+ ast: astroid.Module, root_location: str, am: AstroidManager
64
+ ) -> list:
65
+ imported_modules = []
66
+ for sub_node in ast.body:
67
+ try:
68
+ if isinstance(sub_node, astroid.node_classes.ImportFrom):
69
+ sub_node: astroid.node_classes.ImportFrom = sub_node
70
+
71
+ module_node = am.ast_from_module_name(
72
+ sub_node.modname,
73
+ context_file=root_location,
74
+ )
75
+ imported_modules.append(module_node)
76
+
77
+ elif isinstance(sub_node, astroid.node_classes.Import):
78
+ for name, _ in sub_node.names:
79
+ try:
80
+ module_node = am.ast_from_module_name(
81
+ name,
82
+ context_file=root_location,
83
+ )
84
+ imported_modules.append(module_node)
85
+ except Exception:
86
+ continue
87
+ elif hasattr(sub_node, "body"):
88
+ imported_modules.extend(
89
+ get_imported_modules(sub_node, root_location, am)
90
+ )
91
+
92
+ except astroid.AstroidImportError:
93
+ continue
94
+
95
+ return imported_modules
src/core/bt_graph.py ADDED
@@ -0,0 +1,115 @@
1
+ import json
2
+
3
+ import astroid
4
+ import sys
5
+ import os
6
+
7
+ from src.core.bt_file import BTFile, get_imported_modules
8
+ from src.core.bt_module import BTModule
9
+ from astroid.manager import AstroidManager
10
+
11
+
12
+ class BTGraph:
13
+ DEFAULT_SETTINGS = {"diagram_name": "", "project": None}
14
+ root_module_location: str = None
15
+ target_project_base_location: str = None
16
+ root_module = None
17
+ base_module = None
18
+ am: AstroidManager = None
19
+
20
+ def __init__(self, am: AstroidManager) -> None:
21
+ self.am = am
22
+
23
+ def build_graph(self, config: dict):
24
+ config_path = config.get("_config_path")
25
+ self.root_module_location = os.path.join(config_path, config.get("rootFolder"))
26
+ self.target_project_base_location = config_path
27
+
28
+ sys.path.insert(0, config_path)
29
+ sys.path.insert(1, self.root_module_location)
30
+
31
+ bt_module_list: list[BTModule] = []
32
+
33
+ # Read all the python files within the project
34
+ file_list = self._get_files_recursive(self.root_module_location)
35
+
36
+ # Create modules and add them to module list (No relations between them yet)
37
+ for file in file_list:
38
+ try:
39
+ if not file.endswith("__init__.py"):
40
+ continue
41
+ bt_module = BTModule(file, self.am)
42
+ bt_module.add_files()
43
+ bt_module_list.append(bt_module)
44
+ except Exception as e:
45
+ print(e)
46
+ continue
47
+
48
+ # Add relations between the modules (parent and child nodes)
49
+ for module in bt_module_list:
50
+ for parent_module in bt_module_list:
51
+ if module == parent_module:
52
+ continue
53
+ if parent_module.path == os.path.dirname(module.path):
54
+ parent_module.child_module.append(module)
55
+ module.parent_module = parent_module
56
+
57
+ # Find the root node
58
+ self.root_module = next(
59
+ filter(lambda e: e.parent_module is None, bt_module_list)
60
+ )
61
+ self.base_module = self.root_module
62
+
63
+ # Add dependencies between all the files
64
+ btf_map = self.get_all_bt_files_map()
65
+
66
+ for bt_file in btf_map.values():
67
+ imported_modules = get_imported_modules(
68
+ bt_file.ast, self.target_project_base_location, self.am
69
+ )
70
+ bt_file >> [
71
+ btf_map[module.file]
72
+ for module in imported_modules
73
+ if module.file in btf_map
74
+ ]
75
+
76
+ sys.path = sys.path[2:]
77
+ astroid.manager.AstroidManager().clear_cache()
78
+ self.am.clear_cache()
79
+
80
+ def get_bt_file(self, path: str) -> BTFile:
81
+ file_path = self.am.ast_from_module_name(path).file
82
+ bt_file = self.get_all_bt_files_map()[file_path]
83
+ return bt_file
84
+
85
+ def get_bt_module(self, path: str) -> BTModule:
86
+ path_list = path.split(".")[1:]
87
+ current_module = self.root_module
88
+ while path_list:
89
+ current_module = next(
90
+ filter(
91
+ lambda e: e.name == path_list[0],
92
+ current_module.child_module,
93
+ )
94
+ )
95
+ path_list.pop(0)
96
+ return current_module
97
+
98
+ def change_scope(self, path: str):
99
+ self.root_module = self.get_bt_module(path)
100
+
101
+ def get_all_bt_files_map(self) -> dict[str, BTFile]:
102
+ return {btf.file: btf for btf in self.root_module.get_files_recursive()}
103
+
104
+ def get_all_bt_modules_map(self) -> dict[str, BTModule]:
105
+ return {btm.path: btm for btm in self.root_module.get_submodules_recursive()}
106
+
107
+ def _get_files_recursive(self, path: str) -> list[str]:
108
+ file_list = []
109
+ t = list(os.walk(path))
110
+ for root, _, files in t:
111
+ for file in files:
112
+ file_list.append(os.path.join(root, file))
113
+
114
+ return file_list
115
+