Files
claude-howto/05-skills/code-review/scripts/compare-complexity.py
T
Luong NGUYEN 7db5ade777 Initial commit: Claude How To project
Added comprehensive examples for Claude Code features including slash commands, subagents, memory, MCP protocol, skills, and plugins with documentation and quick reference guides.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 00:09:21 +01:00

160 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""
Compare cyclomatic complexity of code before and after changes.
Helps identify if refactoring actually simplifies code structure.
"""
import re
import sys
from typing import Dict, Tuple
class ComplexityAnalyzer:
"""Analyze code complexity metrics."""
def __init__(self, code: str):
self.code = code
self.lines = code.split('\n')
def calculate_cyclomatic_complexity(self) -> int:
"""
Calculate cyclomatic complexity using McCabe's method.
Count decision points: if, elif, else, for, while, except, and, or
"""
complexity = 1 # Base complexity
# Count decision points
decision_patterns = [
r'\bif\b',
r'\belif\b',
r'\bfor\b',
r'\bwhile\b',
r'\bexcept\b',
r'\band\b(?!$)',
r'\bor\b(?!$)'
]
for pattern in decision_patterns:
matches = re.findall(pattern, self.code)
complexity += len(matches)
return complexity
def calculate_cognitive_complexity(self) -> int:
"""
Calculate cognitive complexity - how hard is it to understand?
Based on nesting depth and control flow.
"""
cognitive = 0
nesting_depth = 0
for line in self.lines:
# Track nesting depth
if re.search(r'^\s*(if|for|while|def|class|try)\b', line):
nesting_depth += 1
cognitive += nesting_depth
elif re.search(r'^\s*(elif|else|except|finally)\b', line):
cognitive += nesting_depth
# Reduce nesting when unindenting
if line and not line[0].isspace():
nesting_depth = 0
return cognitive
def calculate_maintainability_index(self) -> float:
"""
Maintainability Index ranges from 0-100.
> 85: Excellent
> 65: Good
> 50: Fair
< 50: Poor
"""
lines = len(self.lines)
cyclomatic = self.calculate_cyclomatic_complexity()
cognitive = self.calculate_cognitive_complexity()
# Simplified MI calculation
mi = 171 - 5.2 * (cyclomatic / lines) - 0.23 * (cognitive) - 16.2 * (lines / 1000)
return max(0, min(100, mi))
def get_complexity_report(self) -> Dict:
"""Generate comprehensive complexity report."""
return {
'cyclomatic_complexity': self.calculate_cyclomatic_complexity(),
'cognitive_complexity': self.calculate_cognitive_complexity(),
'maintainability_index': round(self.calculate_maintainability_index(), 2),
'lines_of_code': len(self.lines),
'avg_line_length': round(sum(len(l) for l in self.lines) / len(self.lines), 2) if self.lines else 0
}
def compare_files(before_file: str, after_file: str) -> None:
"""Compare complexity metrics between two code versions."""
with open(before_file, 'r') as f:
before_code = f.read()
with open(after_file, 'r') as f:
after_code = f.read()
before_analyzer = ComplexityAnalyzer(before_code)
after_analyzer = ComplexityAnalyzer(after_code)
before_metrics = before_analyzer.get_complexity_report()
after_metrics = after_analyzer.get_complexity_report()
print("=" * 60)
print("CODE COMPLEXITY COMPARISON")
print("=" * 60)
print("\nBEFORE:")
print(f" Cyclomatic Complexity: {before_metrics['cyclomatic_complexity']}")
print(f" Cognitive Complexity: {before_metrics['cognitive_complexity']}")
print(f" Maintainability Index: {before_metrics['maintainability_index']}")
print(f" Lines of Code: {before_metrics['lines_of_code']}")
print(f" Avg Line Length: {before_metrics['avg_line_length']}")
print("\nAFTER:")
print(f" Cyclomatic Complexity: {after_metrics['cyclomatic_complexity']}")
print(f" Cognitive Complexity: {after_metrics['cognitive_complexity']}")
print(f" Maintainability Index: {after_metrics['maintainability_index']}")
print(f" Lines of Code: {after_metrics['lines_of_code']}")
print(f" Avg Line Length: {after_metrics['avg_line_length']}")
print("\nCHANGES:")
cyclomatic_change = after_metrics['cyclomatic_complexity'] - before_metrics['cyclomatic_complexity']
cognitive_change = after_metrics['cognitive_complexity'] - before_metrics['cognitive_complexity']
mi_change = after_metrics['maintainability_index'] - before_metrics['maintainability_index']
loc_change = after_metrics['lines_of_code'] - before_metrics['lines_of_code']
print(f" Cyclomatic Complexity: {cyclomatic_change:+d}")
print(f" Cognitive Complexity: {cognitive_change:+d}")
print(f" Maintainability Index: {mi_change:+.2f}")
print(f" Lines of Code: {loc_change:+d}")
print("\nASSESSMENT:")
if mi_change > 0:
print(" ✅ Code is MORE maintainable")
elif mi_change < 0:
print(" ⚠️ Code is LESS maintainable")
else:
print(" ➡️ Maintainability unchanged")
if cyclomatic_change < 0:
print(" ✅ Complexity DECREASED")
elif cyclomatic_change > 0:
print(" ⚠️ Complexity INCREASED")
else:
print(" ➡️ Complexity unchanged")
print("=" * 60)
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: python compare-complexity.py <before_file> <after_file>")
sys.exit(1)
compare_files(sys.argv[1], sys.argv[2])