#!/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 [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()