-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 563cc53
Showing
9 changed files
with
137 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import ast | ||
import networkx as nx | ||
import matplotlib.pyplot as plt | ||
|
||
class CodeAnalyzer(ast.NodeVisitor): | ||
def __init__(self): | ||
self.G = nx.DiGraph() | ||
self.current_class = None | ||
self.current_function = None # Add this attribute to keep track of the current function | ||
|
||
def visit_ClassDef(self, node): | ||
# Add the class as a node and make it the current context | ||
self.G.add_node(node.name, type='class', color='lightblue', shape='box') | ||
self.current_class = node.name | ||
self.generic_visit(node) | ||
self.current_class = None | ||
|
||
def visit_FunctionDef(self, node): | ||
function_name = f"{self.current_class}.{node.name}" if self.current_class else node.name | ||
self.G.add_node(function_name, type='function', color='lightgreen') | ||
self.current_function = function_name # Set the current function | ||
if self.current_class: | ||
# If we're in a class context, add an edge from the class to the method | ||
self.G.add_edge(self.current_class, function_name, type='contains') | ||
self.generic_visit(node) | ||
self.current_function = None # Reset the current function | ||
|
||
def visit_Call(self, node): | ||
caller_name = self.current_function if self.current_function else self.current_class | ||
callee = None | ||
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name): | ||
callee = f"{node.func.value.id}.{node.func.attr}" | ||
elif isinstance(node.func, ast.Name): | ||
callee = node.func.id | ||
if caller_name and callee: | ||
self.G.add_edge(caller_name, callee, type='calls') | ||
self.generic_visit(node) | ||
|
||
def visit_Assign(self, node): | ||
if isinstance(node.value, ast.Call) and isinstance(node.value.func, ast.Name): | ||
instance_name = ''.join([target.id for target in node.targets if isinstance(target, ast.Name)]) | ||
class_name = node.value.func.id | ||
self.G.add_node(instance_name, type='instance', color='orange') | ||
self.G.add_edge(instance_name, class_name, type='instance_of') | ||
self.generic_visit(node) | ||
|
||
def analyze_code(file_path): | ||
with open(file_path, "r") as source: | ||
node = ast.parse(source.read(), filename=file_path) | ||
analyzer = CodeAnalyzer() | ||
analyzer.visit(node) | ||
|
||
# Set node color based on type, use a default color if 'color' is not present | ||
default_color = 'grey' | ||
node_colors = [data.get('color', default_color) for _, data in analyzer.G.nodes(data=True)] | ||
|
||
# Draw the graph | ||
pos = nx.spring_layout(analyzer.G) | ||
nx.draw(analyzer.G, pos, with_labels=True, node_color=node_colors, arrows=True) | ||
|
||
# Draw edge labels | ||
edge_labels = {(u, v): data['type'] for u, v, data in analyzer.G.edges(data=True) if 'type' in data} | ||
nx.draw_networkx_edge_labels(analyzer.G, pos, edge_labels=edge_labels) | ||
|
||
plt.show() | ||
|
||
|
||
if __name__ == "__main__": | ||
import sys | ||
if len(sys.argv) != 2: | ||
print("Usage: python code_analyzer.py <path_to_python_file>") | ||
else: | ||
analyze_code(sys.argv[1]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Code Structure | ||
digraph { | ||
node [shape=circle] | ||
MyClass [shape=box] | ||
"MyClass.method_a" | ||
"MyClass.method_b" | ||
"self.method_a" -> "MyClass.method_a" | ||
function_c | ||
instance -> MyClass [label="instance of"] | ||
Global -> MyClass | ||
"instance.method_a" -> method_a | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# server/app.py | ||
import sys | ||
import os | ||
|
||
# 将项目根目录(即包含analyzer和visualizer目录的那一级)添加到sys.path | ||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) | ||
|
||
from analyzer.code_analyzer import analyze_code | ||
from visualizer.graph_visualizer import create_graph | ||
from flask import Flask, request, jsonify | ||
|
||
|
||
app = Flask(__name__) | ||
|
||
@app.route('/analyze', methods=['POST']) | ||
def analyze(): | ||
code = request.data.decode("utf-8") | ||
functions, calls = analyze_code(code) | ||
# 为了Demo,这里我们直接返回分析结果,实际应用中可能需要进一步处理 | ||
return jsonify({"functions": functions, "calls": calls}) | ||
|
||
if __name__ == '__main__': | ||
app.run(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
flask | ||
graphviz | ||
pygraphviz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.\venv\Scripts\activate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
class MyClass: | ||
def method_a(self): | ||
pass | ||
|
||
def method_b(self): | ||
self.method_a() | ||
|
||
def function_c(): | ||
instance = MyClass() | ||
instance.method_a() |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# visualizer/graph_visualizer.py | ||
from graphviz import Digraph | ||
|
||
def create_graph(functions, calls): | ||
dot = Digraph(comment='The System Structure') | ||
|
||
for func in functions: | ||
dot.node(func, func) | ||
|
||
for call in set(calls): | ||
if call in functions: | ||
dot.edge(func, call) | ||
|
||
print(dot.source) | ||
dot.render('output/system_structure.gv', view=True) |