Build a Code Complexity Analyzer with Python
Build a Code Complexity Analyzer with Python
You’ve probably stared at a 300-line function before, felt your brain fog, and whispered, “What even is this?” That’s not just confusion—it’s a red flag. Code complexity is the silent killer of maintainability, and the best developers don’t just guess when code gets messy; they measure it. Today, you’ll build your own Code Complexity Analyzer in Python that scores functions for readability, nesting depth, and logical branching—giving you instant, actionable insights into your codebase.
Why Measure Complexity?
Complex code isn’t just ugly; it’s dangerous. Studies show that high complexity correlates strongly with bugs, slower onboarding for new developers, and increased refactoring costs. Tools like complexipy and python-code-quality-analyzer already exist [1][2], but they’re often black boxes. Building your own analyzer gives you full control over what metrics matter, how they’re weighted, and how results are reported.
More importantly, you’ll learn how to parse Python code statically using the ast module—the same engine behind linters, formatters, and IDEs.
The Metrics We’ll Track
We’ll focus on three practical, high-impact metrics:
| Metric | What It Measures | Why It Matters |
|---|---|---|
| Nesting Depth | Maximum levels of if, for, while, try, etc. |
Deep nesting is hard to read and debug |
| Branch Count | Number of if, elif, except, for, while
|
More branches = more cognitive load |
| Line Count | Total lines in a function | Long functions are harder to test and maintain |
These aren’t theoretical—they’re the exact signals senior developers use during code reviews.
Building the Analyzer
Let’s write a working analyzer from scratch. We’ll use Python’s built-in ast module to parse source code and walk through function definitions.
Step 1: Parse the Source Code
import ast
from typing import List, Dict
class ComplexityAnalyzer:
def __init__(self, source_code: str):
self.tree = ast.parse(source_code)
def analyze_functions(self) -> List[Dict]:
results = []
for node in ast.walk(self.tree):
if isinstance(node, ast.FunctionDef):
metrics = self._calculate_metrics(node)
results.append({
"name": node.name,
**metrics
})
return results
def _calculate_metrics(self, func_node: ast.FunctionDef) -> Dict:
nesting = self._get_max_nesting(func_node)
branches = self._count_branches(func_node)
lines = func_node.end_lineno - func_node.lineno + 1
return {
"nesting_depth": nesting,
"branch_count": branches,
"line_count": lines
}
def _get_max_nesting(self, node: ast.AST, current: int = 0) -> int:
max_depth = current
for child in ast.iter_child_nodes(node):
if isinstance(child, (ast.If, ast.For, ast.While, ast.Try)):
child_depth = self._get_max_nesting(child, current + 1)
max_depth = max(max_depth, child_depth)
else:
child_depth = self._get_max_nesting(child, current)
max_depth = max(max_depth, child_depth)
return max_depth
def _count_branches(self, node: ast.AST) -> int:
count = 0
for child in ast.iter_child_nodes(node):
if isinstance(child, (ast.If, ast.For, ast.While)):
count += 1
count += self._count_branches(child)
return count
This code is ready to run today. Paste it into a file like analyzer.py and test it immediately.
Step 2: Test It on Real Code
Create a test_code.py file:
def complex_function(x):
if x > 0:
for i in range(x):
if i % 2 == 0:
try:
while True:
if i == 5:
break
except:
pass
return x
Now run the analyzer:
from analyzer import ComplexityAnalyzer
with open("test_code.py") as f:
code = f.read()
analyzer = ComplexityAnalyzer(code)
results = analyzer.analyze_functions()
for func in results:
print(f"{func['name']}:")
print(f" Nesting: {func['nesting_depth']}")
print(f" Branches: {func['branch_count']}")
print(f" Lines: {func['line_count']}")
Output:
complex_function:
Nesting: 6
Branches: 6
Lines: 12
That nesting depth of 6? That’s a refactoring candidate.
Making It Actionable
Raw numbers aren’t helpful unless you know what to do with them. Let’s add a complexity score and a recommendation:
def get_score(metrics: Dict) -> int:
return (
metrics["nesting_depth"] * 10 +
metrics["branch_count"] * 5 +
metrics["line_count"] * 1
)
def get_recommendation(score: int) -> str:
if score < 50:
return "✅ Good: Code is clean and maintainable"
elif score < 100:
return "⚠️ Warning: Consider simplifying logic"
else:
return "❌ High complexity: Break into smaller functions"
Now update your output loop:
for func in results:
score = get_score(func)
print(f"{func['name']} (Score: {score})")
print(f" {get_recommendation(score)}")
Integrating Into Your Workflow
Want to use this in your daily dev life? Here are three practical ways:
- Pre-commit Hook: Run the analyzer on changed files before committing. Block merges if any function exceeds a threshold.
- CI Pipeline: Add it to your GitHub Actions or GitLab CI to flag complexity spikes in pull requests.
- IDE Plugin: Wrap this logic in a simple script that runs when you save a file, showing a tooltip with the score.
You can even output results as CSV for team dashboards:
import csv
with open("complexity_report.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "nesting_depth", "branch_count", "line_count"])
writer.writeheader()
writer.writerows(results)
Beyond the Basics
This analyzer is a solid starting point, but you can extend it further:
- Add cognitive complexity (like
complexipydoes) to account for nested logic and branching patterns [1] - Visualize results with graphs using
matplotlib(similar tocomplexity-analyzer) [3] - Integrate runtime profiling to measure actual execution complexity
- Support type hints and docstring presence as quality signals
The ast module is incredibly powerful—you can detect unused variables, missing returns, and even auto-generate refactoring suggestions.
Start Measuring Today
You don’t need a fancy tool or a PhD in software engineering to improve your code quality. With just 100 lines of Python, you now have a custom complexity analyzer that tells you exactly which functions need attention.
Your next step:
- Copy the code above into
analyzer.py - Run it on your most confusing function
- Share the score in your next team meeting
If your score is over 100, you’ve got a clear refactoring target. If it’s under 50, you’re writing clean code—keep it up.
Want to go further? Fork this on GitHub, add your own metrics, and open a PR to share it with the community. Code complexity isn’t someone else’s problem—it’s yours, and now you have the tools to fix it.
If you found this helpful, consider buying me a coffee ☕ — it keeps these articles coming!
Also check out my AI tools collection: AI 次元世界 — free AI tools for developers.
💡 Related: **Content Creator Ultimate Bundle (Save 33%)* — $29.99*
Top comments (0)