-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanalyzer.py
More file actions
181 lines (139 loc) · 5.48 KB
/
Copy pathanalyzer.py
File metadata and controls
181 lines (139 loc) · 5.48 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import ast
import os
import openai
from dotenv import load_dotenv
load_dotenv()
openai.api_key = os.getenv("API_KEY")
#openai.ChatCompletion.create()
#client = openai(api_key=api_key)
def extract_source(filepath, node):
with open(filepath, "r") as f:
lines = f.readlines()
# Grab the code block from the AST:
# From the start of the function, to the end
# Then splices them back together
return "".join(lines[node.lineno - 1 : node.end_lineno])
def gpt_suggestions(code_block: str, description: str = "Please suggest improvements for this code"):
"""
Line 25: '''python
Starts a code block so Chat knows to treat it as code
Temperature:
0.0: Deterministic (same output every time)
0.2: Slightly creative
0.7+: Most creative
1.0: Fully random - Throwing ideas straight at a wall- sigma
Temperature controls the randomness
Ovbiously the most optinal thing would be boring
so there is a probability of choosing NOT optimal things
which makes AI a lot cooler
"""
response = openai.ChatCompletion.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": "You are an expert Python developer. Help improve user code by giving useful, clean suggestions."
},
{
"role": "user",
"content": f"{description}:\n\n```python\n{code_block}\n```"
}
],
temperature=0.2
)
return response.choices[0].message.content.strip()
def check_nesting(node, current_depth, issues):
"""
Walk through the AST node function body
Search for nested loops/ifs/whiles within loops
"""
# No loop really needs to pass 3
# No shot you got a 4D matrix
if current_depth > 3:
# 'Store' object has no attribute 'lineno'
# AST nodes issue
line = getattr(node, 'lineno', '?')
issues.append(f"WARNING: Deep nested loop detected at line {line}")
return
# Iterate through child of AST
# Recurse, searching for nested loops
for child in ast.iter_child_nodes(node):
if isinstance(child, (ast.If, ast.For, ast.While, ast.With, ast.Try)):
check_nesting(child, current_depth + 1, issues)
else:
check_nesting(child, current_depth, issues)
def check_unused_imports(tree):
"""
Traverse the AST tree for each node
In each child node, check for Imports or ImportFrom
AST library
Create a set to store from each class
Imported = set()
Imported_From = set()
Used_Import = set()
Check for declared and used variables from the set in the script
"""
imported = set()
imported_from = set()
used_imports = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
imported.add(alias.name)
elif isinstance(node, ast.ImportFrom):
for alias in node.names:
imported_from.add(alias.name)
for node in ast.walk(tree):
if isinstance(node, ast.Name):
used_imports.add(node.id)
unused_imports = [name for name in (imported | imported_from) if name not in used_imports]
return unused_imports
def analyze_file(filepath):
"""
Using the filepath, open it in reading mode and encode it into a AST:
- Walk all noces in the AST
- Append all "problems" into issues list
"""
with open(filepath, "r", encoding="utf-8")as f:
code = f.read()
# try to turn it into tree
try:
tree = ast.parse(code, filename=filepath)
except SyntaxError as e:
return [f"SyntaxError in {filepath}: {e}"]
issues = []
# walk through node in tree
# im walkin it
for node in ast.walk(tree):
# Case 1: long function
if isinstance(node, ast.FunctionDef):
if len(node.body) > 50:
"""
if len(node.body) > 50:
code_block = extract_source(filepath, node)
suggestion = gpt_suggestions(code_block, "This function is too long. Suggest a cleaner version.")
issues.append(f"GPT SUGGESTION for '{node.name}': {suggestion}")
"""
issues.append(
f"WARNING: Function '{node.name}' is too long: {len(node.body)} lines (line {node.lineno})"
)
# Case 2: Global var
if isinstance(node, ast.Global):
issues.append(
f"WARNING: Global variable used at line {node.lineno}"
)
# Case 3: No docstring
if isinstance(node, ast.FunctionDef):
if ast.get_docstring(node) is None:
issues.append(
f"WARNING: No docstring detected in function at line {node.lineno}"
)
# Case 4: Generational nested loops
if isinstance(node, ast.AST):
check_nesting(node, 0, issues)
# Case 5: Unused imports
if isinstance(node, ast.FunctionDef):
not_used_imports = check_unused_imports(tree)
for name in not_used_imports:
issues.append(f"WARNING: Unused import: '{name}'")
return issues