Skip to content

Commit daacbab

Browse files
authored
Added newer examples (#421)
cyclomatic complexity, delete dead code, document functions, pr review bot
1 parent c5ba736 commit daacbab

File tree

8 files changed

+802
-0
lines changed

8 files changed

+802
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Cyclomatic Complexity Analyzer
2+
3+
This example demonstrates how to analyze the cyclomatic complexity of Python codebases using Codegen. The script provides detailed insights into code complexity by analyzing control flow structures and providing a comprehensive report.
4+
5+
> [!NOTE]
6+
> The cyclomatic complexity metric helps identify complex code that might need refactoring. A higher score indicates more complex code with multiple decision points.
7+
8+
## How the Analysis Script Works
9+
10+
The script (`run.py`) performs the complexity analysis in several key steps:
11+
12+
1. **Codebase Loading**
13+
14+
```python
15+
codebase = Codebase.from_repo("fastapi/fastapi")
16+
```
17+
18+
- Loads any Python codebase into Codegen's analysis engine
19+
- Works with local or remote Git repositories
20+
- Supports analyzing specific commits
21+
22+
1. **Complexity Calculation**
23+
24+
```python
25+
def calculate_cyclomatic_complexity(code_block):
26+
complexity = 1 # Base complexity
27+
for statement in code_block.statements:
28+
if isinstance(statement, IfBlockStatement):
29+
complexity += 1 + len(statement.elif_statements)
30+
```
31+
32+
- Analyzes control flow structures (if/elif/else, loops, try/except)
33+
- Calculates complexity based on decision points
34+
- Handles nested structures appropriately
35+
36+
1. **Function Analysis**
37+
38+
```python
39+
callables = codebase.functions + [m for c in codebase.classes for m in c.methods]
40+
for function in callables:
41+
complexity = calculate_cyclomatic_complexity(function.code_block)
42+
```
43+
44+
- Processes both standalone functions and class methods
45+
- Calculates complexity for each callable
46+
- Tracks file locations and function names
47+
48+
1. **Report Generation**
49+
50+
```python
51+
print("\n📊 Cyclomatic Complexity Analysis")
52+
print(f" • Total Functions: {total_functions}")
53+
print(f" • Average Complexity: {average:.2f}")
54+
```
55+
56+
- Provides comprehensive complexity statistics
57+
- Shows distribution of complexity across functions
58+
- Identifies the most complex functions
59+
60+
## Output
61+
62+
```
63+
📊 Cyclomatic Complexity Analysis
64+
============================================================
65+
66+
📈 Overall Stats:
67+
• Total Functions: 3538
68+
• Average Complexity: 1.27
69+
• Total Complexity: 4478
70+
71+
🔍 Top 10 Most Complex Functions:
72+
------------------------------------------------------------
73+
• jsonable_encoder 16 | fastapi/encoders.py
74+
• get_openapi 13 | fastapi/openapi/utils.py
75+
• __init__ 12 | fastapi/routing.py
76+
• solve_dependencies 10 | fastapi/dependencies/utils.py
77+
• main 9 | scripts/notify_translations.py
78+
• analyze_param 9 | fastapi/dependencies/utils.py
79+
• __init__ 8 | fastapi/params.py
80+
• __init__ 8 | fastapi/params.py
81+
• main 7 | scripts/deploy_docs_status.py
82+
• create_model_field 7 | fastapi/utils.py
83+
84+
📉 Complexity Distribution:
85+
• Low (1-5): 3514 functions (99.3%)
86+
• Medium (6-10): 21 functions (0.6%)
87+
• High (>10): 3 functions (0.1%)
88+
```
89+
90+
## Complexity Metrics
91+
92+
The analyzer tracks several key metrics:
93+
94+
### Complexity Sources
95+
96+
- If statements (+1)
97+
- Elif statements (+1 each)
98+
- Else statements (+1)
99+
- Loops (while/for) (+1)
100+
- Try-except blocks (+1 per except)
101+
102+
### Complexity Categories
103+
104+
- Low (1-5): Generally clean and maintainable code
105+
- Medium (6-10): Moderate complexity, may need attention
106+
- High (>10): Complex code that should be reviewed
107+
108+
## Running the Analysis
109+
110+
```bash
111+
# Install Codegen
112+
pip install codegen
113+
114+
# Run the analysis
115+
python run.py
116+
```
117+
118+
## Example Output
119+
120+
```
121+
📊 Cyclomatic Complexity Analysis
122+
============================================================
123+
124+
📈 Overall Stats:
125+
• Total Functions: 150
126+
• Average Complexity: 3.45
127+
• Total Complexity: 518
128+
129+
🔍 Top 10 Most Complex Functions:
130+
------------------------------------------------------------
131+
• validate_response 12 | ...api/endpoints/auth.py
132+
• process_request 10 | ...core/middleware.py
133+
• handle_exception 9 | ...utils/error_handlers.py
134+
135+
📉 Complexity Distribution:
136+
• Low (1-5): 105 functions (70.0%)
137+
• Medium (6-10): 35 functions (23.3%)
138+
• High (>10): 10 functions (6.7%)
139+
```
140+
141+
## Learn More
142+
143+
- [About Cyclomatic Complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity)
144+
- [Codegen Documentation](https://docs.codegen.com)
145+
146+
## Contributing
147+
148+
Feel free to submit issues and enhancement requests!
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import codegen
2+
from codegen import Codebase
3+
from codegen.sdk.enums import ProgrammingLanguage
4+
from codegen.sdk.core.statements.for_loop_statement import ForLoopStatement
5+
from codegen.sdk.core.statements.if_block_statement import IfBlockStatement
6+
from codegen.sdk.core.statements.try_catch_statement import TryCatchStatement
7+
from codegen.sdk.core.statements.while_statement import WhileStatement
8+
9+
10+
@codegen.function("cyclomatic-complexity")
11+
def run(codebase: Codebase):
12+
def calculate_cyclomatic_complexity(code_block):
13+
# Initialize cyclomatic complexity count
14+
complexity = 1 # Start with one for the default path
15+
16+
# Count decision points
17+
for statement in code_block.statements:
18+
if isinstance(statement, IfBlockStatement):
19+
complexity += 1 + len(statement.elif_statements) # +1 for if, each elif adds another path
20+
if statement.else_statement:
21+
complexity += 1
22+
elif isinstance(statement, WhileStatement) or isinstance(statement, ForLoopStatement):
23+
complexity += 1 # Loops introduce a new path
24+
elif isinstance(statement, TryCatchStatement):
25+
complexity += 1 # try-catch introduces a new path
26+
# Count except blocks by counting nested code blocks after the first one (try block)
27+
complexity += len(statement.nested_code_blocks) - 1 # -1 to exclude the try block itself
28+
29+
return complexity
30+
31+
# Initialize total complexity
32+
total_complexity = 0
33+
# Count total functions
34+
total_functions = 0
35+
# Store results for sorting
36+
results = []
37+
38+
# Get all functions or methods
39+
callables = codebase.functions + [m for c in codebase.classes for m in c.methods]
40+
41+
# Analyze each function
42+
for function in callables:
43+
complexity = calculate_cyclomatic_complexity(function.code_block)
44+
results.append((function.name, complexity, function.filepath))
45+
total_complexity += complexity
46+
total_functions += 1
47+
48+
# Sort by complexity (highest first)
49+
results.sort(key=lambda x: x[1], reverse=True)
50+
51+
# Print summary
52+
print("\n📊 Cyclomatic Complexity Analysis")
53+
print("=" * 60)
54+
55+
if total_functions > 0:
56+
average = total_complexity / total_functions
57+
print("\n📈 Overall Stats:")
58+
print(f" • Total Functions: {total_functions}")
59+
print(f" • Average Complexity: {average:.2f}")
60+
print(f" • Total Complexity: {total_complexity}")
61+
62+
print("\n🔍 Top 10 Most Complex Functions:")
63+
print("-" * 60)
64+
for name, complexity, filepath in results[:10]:
65+
# Truncate filepath if too long
66+
if len(filepath) > 40:
67+
filepath = "..." + filepath[-37:]
68+
print(f" • {name:<30} {complexity:>3} | {filepath}")
69+
70+
# Complexity distribution
71+
low = sum(1 for _, c, _ in results if c <= 5)
72+
medium = sum(1 for _, c, _ in results if 5 < c <= 10)
73+
high = sum(1 for _, c, _ in results if c > 10)
74+
75+
print("\n📉 Complexity Distribution:")
76+
print(f" • Low (1-5): {low} functions ({low / total_functions * 100:.1f}%)")
77+
print(f" • Medium (6-10): {medium} functions ({medium / total_functions * 100:.1f}%)")
78+
print(f" • High (>10): {high} functions ({high / total_functions * 100:.1f}%)")
79+
else:
80+
print("❌ No functions found in the codebase to analyze.")
81+
82+
83+
if __name__ == "__main__":
84+
print("🔍 Analyzing codebase...")
85+
codebase = Codebase.from_repo("fastapi/fastapi", commit="887270ff8a54bb58c406b0651678a27589793d2f", programming_language=ProgrammingLanguage.PYTHON)
86+
87+
print("Running analysis...")
88+
run(codebase)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Delete Dead Code
2+
3+
This example demonstrates how to identify and remove dead code from a codebase using Codegen. The script efficiently cleans up unused functions and variables, helping maintain a lean and efficient codebase.
4+
5+
> [!NOTE]
6+
> Dead code refers to code that is not being used or referenced anywhere in your codebase. However, some code might appear unused but should not be deleted, such as test files, functions with decorators, public API endpoints, and event handlers.
7+
8+
## How the Dead Code Removal Script Works
9+
10+
The script (`run.py`) performs the dead code removal in several key steps:
11+
12+
1. **Codebase Loading**
13+
14+
```python
15+
codebase = Codebase.from_repo("tox-dev/tox", programming_language=ProgrammingLanguage.PYTHON)
16+
```
17+
18+
- Loads a codebase using the `Codebase.from_repo` method
19+
- This example uses the `tox-dev/tox` repository because it is mostly self-contained
20+
21+
1. **Function Removal**
22+
23+
```python
24+
for function in codebase.functions:
25+
if "test" in function.file.filepath:
26+
continue
27+
if function.decorators:
28+
continue
29+
if not function.usages and not function.call_sites:
30+
print(f"🗑️ Removing unused function: {function.name}")
31+
function.remove()
32+
```
33+
34+
- Skips test files and decorated functions
35+
- Removes functions with no usages or call sites
36+
37+
1. **Variable Cleanup**
38+
39+
```python
40+
for func in codebase.functions:
41+
for var_assignments in func.code_block.local_var_assignments:
42+
if not var_assignments.local_usages:
43+
print(f"🧹 Removing unused variable: {var_assignments.name}")
44+
var_assignments.remove()
45+
```
46+
47+
- Iterates through local variable assignments
48+
- Removes variables with no local usages
49+
50+
## Running the Script
51+
52+
```bash
53+
# Install Codegen
54+
pip install codegen
55+
56+
# Run the script
57+
python run.py
58+
```
59+
60+
## Example Output
61+
62+
```
63+
� Deleting dead code...
64+
65+
🗑️ Removing unused function: _get_parser_doc
66+
🧹 Removing unused variable: decoded
67+
🧹 Removing unused variable: shebang_line
68+
...
69+
🧹 Removing unused variable: _
70+
71+
🔧 Total functions removed: 2
72+
📦 Total variables removed: 240
73+
```
74+
75+
## Learn More
76+
77+
- [Deleting Dead Code](https://docs.codegen.com/tutorials/deleting-dead-code)
78+
- [Codegen Documentation](https://docs.codegen.com)
79+
80+
## Contributing
81+
82+
Feel free to submit issues and enhancement requests!
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import codegen
2+
from codegen import Codebase
3+
from codegen.sdk.enums import ProgrammingLanguage
4+
5+
6+
@codegen.function("delete-dead-code")
7+
def run(codebase: Codebase):
8+
removed_functions_count = 0
9+
removed_variables_count = 0
10+
11+
for function in codebase.functions:
12+
# Skip test files
13+
if "test" in function.file.filepath:
14+
continue
15+
16+
# Skip decorated functions
17+
if function.decorators:
18+
continue
19+
20+
# Check if the function has no usages and no call sites
21+
if not function.usages and not function.call_sites:
22+
print(f"🗑️ Removing unused function: {function.name}")
23+
function.remove()
24+
removed_functions_count += 1
25+
26+
# Clean up unused variables
27+
for func in codebase.functions:
28+
for var_assignments in func.code_block.local_var_assignments:
29+
if not var_assignments.local_usages:
30+
print(f"🧹 Removing unused variable: {var_assignments.name}")
31+
var_assignments.remove()
32+
removed_variables_count += 1
33+
34+
print("\n")
35+
print(f"🔧 Total functions removed: {removed_functions_count}")
36+
print(f"📦 Total variables removed: {removed_variables_count}")
37+
38+
39+
if __name__ == "__main__":
40+
print("🔍 Analyzing codebase...")
41+
codebase = Codebase.from_repo("tox-dev/tox", programming_language=ProgrammingLanguage.PYTHON, commit="b588b696e0940c1813014b31b68d7660d8a1914f")
42+
43+
print("🚮 Deleting dead code...")
44+
run(codebase)

0 commit comments

Comments
 (0)