-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode_analyzer.py
More file actions
119 lines (102 loc) · 3.65 KB
/
Copy pathcode_analyzer.py
File metadata and controls
119 lines (102 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import ast
import os
def extract_code_structure(file_path):
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
source = f.read()
except Exception as e:
return None, str(e)
try:
tree = ast.parse(source)
except SyntaxError as e:
return None, f"Syntax error: {e}"
structure = {
'file_path': file_path,
'source': source,
'functions': [],
'classes': [],
'imports': [],
'module_docstring': ast.get_docstring(tree) or '',
'line_count': len(source.splitlines()),
'char_count': len(source)
}
for node in ast.walk(tree):
if isinstance(node, (ast.Import, ast.ImportFrom)):
if isinstance(node, ast.Import):
for alias in node.names:
structure['imports'].append(alias.name)
else:
module = node.module or ''
for alias in node.names:
structure['imports'].append(f"{module}.{alias.name}" if module else alias.name)
for node in ast.iter_child_nodes(tree):
if isinstance(node, ast.FunctionDef) or isinstance(node, ast.AsyncFunctionDef):
func_info = extract_function_info(node, source)
structure['functions'].append(func_info)
elif isinstance(node, ast.ClassDef):
class_info = extract_class_info(node, source)
structure['classes'].append(class_info)
return structure, None
def extract_function_info(node, source):
args = []
for arg in node.args.args:
arg_info = {'name': arg.arg, 'annotation': ''}
if arg.annotation:
try:
arg_info['annotation'] = ast.unparse(arg.annotation)
except:
pass
args.append(arg_info)
return_annotation = ''
if node.returns:
try:
return_annotation = ast.unparse(node.returns)
except:
pass
lines = source.splitlines()
start = node.lineno - 1
end = min(node.end_lineno, start + 30)
snippet = '\n'.join(lines[start:end])
return {
'name': node.name,
'args': args,
'return_annotation': return_annotation,
'docstring': ast.get_docstring(node) or '',
'line_number': node.lineno,
'is_async': isinstance(node, ast.AsyncFunctionDef),
'decorator_list': [ast.unparse(d) for d in node.decorator_list if hasattr(ast, 'unparse')],
'snippet': snippet
}
def extract_class_info(node, source):
methods = []
for item in ast.iter_child_nodes(node):
if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
methods.append(extract_function_info(item, source))
bases = []
for base in node.bases:
try:
bases.append(ast.unparse(base))
except:
pass
return {
'name': node.name,
'bases': bases,
'docstring': ast.get_docstring(node) or '',
'methods': methods,
'line_number': node.lineno
}
def scan_project(project_path):
py_files = []
exclude_dirs = {'venv', '__pycache__', '.git', 'node_modules', '.pytest_cache', 'dist', 'build', 'eggs'}
for root, dirs, files in os.walk(project_path):
dirs[:] = [d for d in dirs if d not in exclude_dirs]
for file in files:
if file.endswith('.py'):
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, project_path)
py_files.append({
'full_path': full_path,
'relative_path': rel_path,
'filename': file
})
return py_files