Skip to content

Commit 6427fe2

Browse files
committed
computes cyclomatic complexity
1 parent 04ce83b commit 6427fe2

File tree

5 files changed

+125
-0
lines changed

5 files changed

+125
-0
lines changed

β€Ž.codegen/.gitignoreβ€Ž

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Codegen
2+
docs/
3+
examples/
4+
prompts/
5+
jupyter/
6+
.venv/
7+
codegen-system-prompt.txt
8+
9+
# Python cache files
10+
__pycache__/
11+
*.py[cod]
12+
*$py.class
13+
14+
# Keep config.toml and codemods
15+
!config.toml
16+
!codemods/
17+
!codemods/**
Binary file not shown.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import codegen
2+
from codegen import Codebase
3+
4+
5+
@codegen.function("test-function")
6+
def run(codebase: Codebase):
7+
"""Add a description of what this codemod does."""
8+
# Add your code here
9+
print("Total files: ", len(codebase.files))
10+
print("Total functions: ", len(codebase.functions))
11+
print("Total imports: ", len(codebase.imports))
12+
13+
14+
if __name__ == "__main__":
15+
print("Parsing codebase...")
16+
codebase = Codebase("./")
17+
18+
print("Running...")
19+
run(codebase)

β€Ž.codegen/config.tomlβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
organization_name = "codegen-sh"
2+
repo_name = "codegen-examples"
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import codegen
2+
from codegen import Codebase
3+
from codegen.sdk.core.statements.for_loop_statement import ForLoopStatement
4+
from codegen.sdk.core.statements.if_block_statement import IfBlockStatement
5+
from codegen.sdk.core.statements.try_catch_statement import TryCatchStatement
6+
from codegen.sdk.core.statements.while_statement import WhileStatement
7+
8+
9+
@codegen.function("cyclomatic-complexity")
10+
def run(codebase: Codebase):
11+
def calculate_cyclomatic_complexity(code_block):
12+
# Initialize cyclomatic complexity count
13+
complexity = 1 # Start with one for the default path
14+
15+
# Count decision points
16+
for statement in code_block.statements:
17+
if isinstance(statement, IfBlockStatement):
18+
complexity += 1 + len(statement.elif_statements) # +1 for if, each elif adds another path
19+
if statement.else_statement:
20+
complexity += 1
21+
elif isinstance(statement, WhileStatement) or isinstance(statement, ForLoopStatement):
22+
complexity += 1 # Loops introduce a new path
23+
elif isinstance(statement, TryCatchStatement):
24+
complexity += 1 # try-catch introduces a new path
25+
# Count except blocks by counting nested code blocks after the first one (try block)
26+
complexity += len(statement.nested_code_blocks) - 1 # -1 to exclude the try block itself
27+
28+
return complexity
29+
30+
# Initialize total complexity
31+
total_complexity = 0
32+
# Count total functions
33+
total_functions = 0
34+
# Store results for sorting
35+
results = []
36+
37+
# Get all functions or methods
38+
callables = codebase.functions + [m for c in codebase.classes for m in c.methods]
39+
40+
# Analyze each function
41+
for function in callables:
42+
complexity = calculate_cyclomatic_complexity(function.code_block)
43+
results.append((function.name, complexity, function.filepath))
44+
total_complexity += complexity
45+
total_functions += 1
46+
47+
# Sort by complexity (highest first)
48+
results.sort(key=lambda x: x[1], reverse=True)
49+
50+
# Print summary
51+
print("\nπŸ“Š Cyclomatic Complexity Analysis")
52+
print("=" * 60)
53+
54+
if total_functions > 0:
55+
average = total_complexity / total_functions
56+
print("\nπŸ“ˆ Overall Stats:")
57+
print(f" β€’ Total Functions: {total_functions}")
58+
print(f" β€’ Average Complexity: {average:.2f}")
59+
print(f" β€’ Total Complexity: {total_complexity}")
60+
61+
print("\nπŸ” Top 10 Most Complex Functions:")
62+
print("-" * 60)
63+
for name, complexity, filepath in results[:10]:
64+
# Truncate filepath if too long
65+
if len(filepath) > 40:
66+
filepath = "..." + filepath[-37:]
67+
print(f" β€’ {name:<30} {complexity:>3} | {filepath}")
68+
69+
# Complexity distribution
70+
low = sum(1 for _, c, _ in results if c <= 5)
71+
medium = sum(1 for _, c, _ in results if 5 < c <= 10)
72+
high = sum(1 for _, c, _ in results if c > 10)
73+
74+
print("\nπŸ“‰ Complexity Distribution:")
75+
print(f" β€’ Low (1-5): {low} functions ({low / total_functions * 100:.1f}%)")
76+
print(f" β€’ Medium (6-10): {medium} functions ({medium / total_functions * 100:.1f}%)")
77+
print(f" β€’ High (>10): {high} functions ({high / total_functions * 100:.1f}%)")
78+
else:
79+
print("❌ No functions found in the codebase to analyze.")
80+
81+
82+
if __name__ == "__main__":
83+
print("πŸ” Analyzing codebase...")
84+
codebase = Codebase.from_repo("fastapi/fastapi", commit="887270ff8a54bb58c406b0651678a27589793d2f")
85+
86+
print("Running analysis...")
87+
run(codebase)

0 commit comments

Comments
Β (0)