Coverage for src/renaissance/integrations/python/ast/extractor.py: 84%
32 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-15 10:34 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-15 10:34 +0000
1# Copyright (C) 2024-2026 TNO
2#
3# Licensed under the terms specified in the LICENSE file in the root directory
4# of this repository, also available at:
5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE
7"""AI: Extractor that builds a codebase graph of Python modules and their contained statements."""
9from pathlib import Path
11import networkx as nx
13from renaissance.integrations.python.ast.rst_node import PythonRstNode
14from renaissance.syntax_tree.semantic_kind import SemanticKind
17class PythonExtractor:
18 """AI: Extractor that builds a codebase graph of Python modules and their contained statements."""
20 graph = nx.DiGraph()
21 codebase: dict = {}
23 def process(self, file: Path) -> None:
24 """AI: Parse file and merge its module/import/function/class nodes into the codebase graph."""
25 root = PythonRstNode.load(file)
26 module_name = root.filename.replace("/", ".").replace(".py", "")
27 folder = str(Path(file).parent)
28 self.graph.add_node(folder, type="folder")
29 self.graph.add_edge(folder, module_name, type="contains")
31 for stmt in root:
32 match stmt.semantic_kind:
33 case SemanticKind.IMPORT:
34 self.graph.add_edge(module_name, stmt.name, type="include")
35 case _ if stmt.parser_kind == "ImportFrom":
36 for alias in stmt.node.names:
37 self.graph.add_edge(module_name, f"{stmt.node.module}.{alias.name}", type="include")
38 case SemanticKind.FUNCTION:
39 self.graph.add_edge(module_name, f"{module_name}.{stmt.name}", type="definition")
40 self.graph.add_node(f"{module_name}.{stmt.name}", properties="function")
41 # TODO: convert #, stmt.properties) to graphml
42 case SemanticKind.CLASS:
43 self.graph.add_edge(module_name, f"{module_name}.{stmt.name}", type="definition")
44 self.graph.add_node(f"{module_name}.{stmt.name}") # convert to args, stmt.properties)
45 case _:
46 pass
48 self.codebase[file] = root
49 # # reconstruct dependencies inside module
50 # tu.lazy_create_refers(root)
51 # self.nodes |= tu._nodes
52 # self.edges |=tu._references
53 # self.edges |= tu._referenced_by
55 def save_graph(self, filename: str) -> None:
56 """AI: Write the extracted codebase graph to a GraphML file named filename."""
57 nx.write_graphml(self.graph, filename)
58 print(f"Graph saved to: {filename}")