File size: 6,020 Bytes
5854ce9 |
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 |
import ast
from typing import Dict, List, Any, Optional
import re
class CodeInterpreter:
def __init__(self):
self.language = 'python' # Only support Python
def analyze_code(self, code: str) -> Dict[str, Any]:
"""
Analyze Python code and extract key information about its structure and functionality.
"""
try:
return self._analyze_python_code(code)
except Exception as e:
return {"error": f"Python code analysis failed: {str(e)}"}
def _analyze_python_code(self, code: str) -> Dict[str, Any]:
"""
Analyze Python code using AST.
"""
try:
tree = ast.parse(code)
analysis = {
"imports": [],
"functions": [],
"classes": [],
"variables": [],
"complexity": 0,
"docstrings": [],
"decorators": []
}
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for name in node.names:
analysis["imports"].append(name.name)
elif isinstance(node, ast.ImportFrom):
analysis["imports"].append(f"{node.module}.{node.names[0].name}")
elif isinstance(node, ast.FunctionDef):
func_info = {
"name": node.name,
"args": [arg.arg for arg in node.args.args],
"returns": self._get_return_type(node),
"complexity": self._calculate_complexity(node),
"docstring": ast.get_docstring(node),
"decorators": [d.id for d in node.decorator_list if isinstance(d, ast.Name)]
}
analysis["functions"].append(func_info)
elif isinstance(node, ast.ClassDef):
class_info = {
"name": node.name,
"methods": [],
"bases": [base.id for base in node.bases if isinstance(base, ast.Name)],
"docstring": ast.get_docstring(node),
"decorators": [d.id for d in node.decorator_list if isinstance(d, ast.Name)]
}
for item in node.body:
if isinstance(item, ast.FunctionDef):
class_info["methods"].append(item.name)
analysis["classes"].append(class_info)
elif isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name):
analysis["variables"].append(target.id)
elif isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):
analysis["docstrings"].append(node.value.s)
analysis["complexity"] = sum(func["complexity"] for func in analysis["functions"])
return analysis
except Exception as e:
return {"error": f"Python code analysis failed: {str(e)}"}
def _get_return_type(self, node: ast.FunctionDef) -> Optional[str]:
"""Extract return type annotation if present."""
if node.returns:
if isinstance(node.returns, ast.Name):
return node.returns.id
elif isinstance(node.returns, ast.Subscript):
return f"{node.returns.value.id}[{node.returns.slice.value.id}]"
return None
def _calculate_complexity(self, node: ast.AST) -> int:
"""Calculate cyclomatic complexity of a function."""
complexity = 1
for child in ast.walk(node):
if isinstance(child, (ast.If, ast.While, ast.For, ast.Try, ast.ExceptHandler)):
complexity += 1
return complexity
def suggest_improvements(self, analysis: Dict[str, Any]) -> List[str]:
"""
Suggest code improvements based on analysis.
"""
suggestions = []
# Check function complexity
for func in analysis.get("functions", []):
if func["complexity"] > 10:
suggestions.append(f"Function '{func['name']}' is too complex (complexity: {func['complexity']}). Consider breaking it down into smaller functions.")
# Check for missing type hints
for func in analysis.get("functions", []):
if not func["returns"]:
suggestions.append(f"Function '{func['name']}' is missing return type annotation.")
# Check for missing docstrings
for func in analysis.get("functions", []):
if not func["docstring"]:
suggestions.append(f"Function '{func['name']}' is missing a docstring.")
# Check for unused imports
if len(analysis.get("imports", [])) > 10:
suggestions.append("Consider removing unused imports to improve code clarity.")
# Check for long functions
for func in analysis.get("functions", []):
if len(func["args"]) > 5:
suggestions.append(f"Function '{func['name']}' has too many parameters ({len(func['args'])}). Consider using a data class or dictionary.")
return suggestions
def extract_code_context(self, code: str, line_number: int) -> Dict[str, Any]:
"""
Extract context around a specific line of code.
"""
lines = code.split('\n')
context = {
"line": lines[line_number - 1] if 0 <= line_number - 1 < len(lines) else "",
"before": lines[max(0, line_number - 3):line_number - 1],
"after": lines[line_number:min(len(lines), line_number + 3)],
"indentation": len(re.match(r'^\s*', lines[line_number - 1]).group()) if 0 <= line_number - 1 < len(lines) else 0
}
return context |