99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Quick Python file analyzer - extracts basic metrics from Python source files.
|
|
Line count, function count, class count, imports, docstrings.
|
|
"""
|
|
|
|
import ast
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def analyze_python_file(filepath: str) -> dict:
|
|
"""Analyze a Python file and return metrics."""
|
|
path = Path(filepath)
|
|
if not path.exists():
|
|
return {"error": f"File not found: {filepath}"}
|
|
|
|
content = path.read_text()
|
|
lines = content.splitlines()
|
|
|
|
result = {
|
|
"file": filepath,
|
|
"total_lines": len(lines),
|
|
"code_lines": 0,
|
|
"comment_lines": 0,
|
|
"blank_lines": 0,
|
|
"functions": 0,
|
|
"classes": 0,
|
|
"imports": 0,
|
|
"docstrings": 0,
|
|
}
|
|
|
|
# Count line types
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
if not stripped:
|
|
result["blank_lines"] += 1
|
|
elif stripped.startswith("#"):
|
|
result["comment_lines"] += 1
|
|
else:
|
|
result["code_lines"] += 1
|
|
|
|
# Parse AST for structural metrics
|
|
try:
|
|
tree = ast.parse(content)
|
|
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.FunctionDef):
|
|
result["functions"] += 1
|
|
elif isinstance(node, ast.ClassDef):
|
|
result["classes"] += 1
|
|
elif isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
result["imports"] += 1
|
|
|
|
# Count module docstrings
|
|
if (tree.body and isinstance(tree.body[0], ast.Expr) and
|
|
isinstance(tree.body[0].value, ast.Constant) and
|
|
isinstance(tree.body[0].value.value, str)):
|
|
result["docstrings"] += 1
|
|
|
|
except SyntaxError as e:
|
|
result["ast_error"] = str(e)
|
|
|
|
return result
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: py_analyzer.py <file1.py> [file2.py] ...")
|
|
sys.exit(1)
|
|
|
|
files = sys.argv[1:]
|
|
|
|
for filepath in files:
|
|
result = analyze_python_file(filepath)
|
|
print(f"\n{'='*60}")
|
|
print(f"Analysis: {filepath}")
|
|
print('='*60)
|
|
if "error" in result:
|
|
print(f" ERROR: {result['error']}")
|
|
continue
|
|
|
|
print(f" Total lines: {result['total_lines']}")
|
|
print(f" Code lines: {result['code_lines']}")
|
|
print(f" Comment lines: {result['comment_lines']}")
|
|
print(f" Blank lines: {result['blank_lines']}")
|
|
print(f" Functions: {result['functions']}")
|
|
print(f" Classes: {result['classes']}")
|
|
print(f" Imports: {result['imports']}")
|
|
print(f" Docstrings: {result['docstrings']}")
|
|
|
|
# Quick quality check
|
|
cyclomatic = result['code_lines'] - (result['functions'] + result['classes']) + 1
|
|
complexity = "HIGH" if cyclomatic > 50 else "MODERATE" if cyclomatic > 20 else "LOW"
|
|
print(f" Cyclomatic est: {cyclomatic} ({complexity} complexity)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |