Python Code Simplifier
Transform complex, hard-to-maintain Python code into clean, readable, idiomatic solutions.
Analysis Scripts
# Comprehensive analysis (runs all checks)
python scripts/analyze_all.py /path/to/project
# Individual analyzers:
python scripts/analyze_complexity.py . # Cyclomatic/cognitive complexity
python scripts/find_code_smells.py . # Mutable defaults, bare excepts, etc.
python scripts/find_overengineering.py . # YAGNI violations, unused abstractions
python scripts/find_dead_code.py . # Unused imports, functions, variables
python scripts/find_unpythonic.py . # Non-idiomatic patterns
python scripts/find_coupling_issues.py . # Feature envy, low cohesion
python scripts/find_duplicates.py . # Structural duplicate detection
# JSON output for CI/tooling
python scripts/analyze_all.py . --format json > report.json
Workflow
- Analyze: Run
analyze_all.py to identify all issues
- Prioritize: Address high-severity issues (🔴) first
- Simplify: Apply patterns below incrementally
- Verify: Ensure simplified code is functionally equivalent
Simplification Principles
- YAGNI: Don't add abstractions until needed
- Preserve behavior: Simplification ≠ changing functionality
- One change at a time: Incremental is safer
- Readability over cleverness: Clear beats "smart"
- Keep related code together: Locality matters
Common Simplification Patterns
Extract and Name
# Before: Complex inline condition
if user.age >= 18 and user.country in ALLOWED and not user.banned:
# After: Named condition
is_eligible = user.age >= 18 and user.country in ALLOWED and not user.banned
if is_eligible:
Early Returns
# Before: Deep nesting
def process(data):
if data:
if data.valid:
if data.ready:
return compute(data)
return None
# After: Guard clauses
def process(data):
if not data or not data.valid or not data.ready:
return None
return compute(data)
Comprehensions
# Before: Manual loop
result = []
for item in items:
if item.active:
result.append(item.name)
# After: List comprehension
result = [item.name for item in items if item.active]
Dictionary Techniques
# Before: Verbose key checking
if key in d:
value = d[key]
else:
value = default
# After: get() with default
value = d.get(key, default)
# Before: Manual grouping
groups = {}
for item in items:
if item.category not in groups:
groups[item.category] = []
groups[item.category].append(item)
# After: defaultdict
from collections import defaultdict
groups = defaultdict(list)
for item in items:
groups[item.category].append(item)
Context Managers
# Before: Manual cleanup
f = open('file.txt')
try:
data = f.read()
finally:
f.close()
# After: with statement
with open('file.txt') as f:
data = f.read()
Over-Engineering Anti-Patterns
| Pattern |
Problem |
Solution |
| Single-impl interface |
Abstract class with one subclass |
Merge or wait for need |
| Unnecessary factory |
Factory that creates one type |
Direct instantiation |
| Premature strategy |
Strategy pattern with one strategy |
Simple function |
| Thin wrapper |
Class that just delegates |
Use wrapped class directly |
| Speculative generality |
Code for "future needs" |
Delete it (YAGNI) |
| Deep inheritance |
4+ levels of inheritance |
Composition over inheritance |
Code Smells Quick Reference
| Smell |
Detection |
Fix |
| Mutable default |
def f(x=[]) |
Use None, create inside |
| Bare except |
except: |
except Exception: |
| God class |
15+ methods, 10+ attrs |
Split into focused classes |
| Long function |
50+ lines |
Extract helper functions |
| Deep nesting |
4+ levels |
Early returns, extract |
| Feature envy |
Method uses other class more |
Move method |
| Magic numbers |
Unexplained numeric literals |
Named constants |
Script Reference
| Script |
What It Detects |
analyze_complexity.py |
Cyclomatic complexity, cognitive complexity, nesting depth, function length, parameter count, class size |
find_code_smells.py |
Mutable defaults, bare excepts, magic numbers, type comparisons, god classes, data classes, boolean blindness |
find_overengineering.py |
Single-implementation interfaces, unused abstractions, unnecessary factories/builders, thin wrappers, premature strategies |
find_dead_code.py |
Unused imports, unused functions/classes, unused parameters, unreachable code, constant conditions |
find_unpythonic.py |
range(len()), == True/False/None, swallowed exceptions, manual index tracking |
find_coupling_issues.py |
Feature envy, low cohesion (LCOM), message chains, middle man classes |
find_duplicates.py |
Structurally similar code blocks using AST normalization |
When NOT to Simplify
- Working legacy code with no tests
- Performance-critical hot paths (measure first)
- Code that will be replaced soon
- External API constraints requiring complexity
1---2name: python-simplifier3description: Simplify overly complex Python code. Use when user asks to simplify, refactor, clean up, make more readable, reduce complexity, improve code quality, find code smells, detect duplicates, or analyze coupling in Python code. Triggers on requests like "simplify this code", "this is too complex", "make this more readable", "refactor this", "clean this up", "find issues", "analyze this codebase", or when reviewing code that exhibits complexity anti-patterns. For Django-specific analysis, use the django-simplifier skill instead.4---5
6# Python Code Simplifier
7
8Transform complex, hard-to-maintain Python code into clean, readable, idiomatic solutions.
9
10## Analysis Scripts
11
12```bash
13# Comprehensive analysis (runs all checks)
14python scripts/analyze_all.py /path/to/project
15
16# Individual analyzers:
17python scripts/analyze_complexity.py . # Cyclomatic/cognitive complexity
18python scripts/find_code_smells.py . # Mutable defaults, bare excepts, etc.
19python scripts/find_overengineering.py . # YAGNI violations, unused abstractions
20python scripts/find_dead_code.py . # Unused imports, functions, variables
21python scripts/find_unpythonic.py . # Non-idiomatic patterns
22python scripts/find_coupling_issues.py . # Feature envy, low cohesion
23python scripts/find_duplicates.py . # Structural duplicate detection
24
25# JSON output for CI/tooling
26python scripts/analyze_all.py . --format json > report.json
27```
28
29## Workflow
30
311. **Analyze**: Run `analyze_all.py` to identify all issues
322. **Prioritize**: Address high-severity issues (🔴) first
333. **Simplify**: Apply patterns below incrementally
344. **Verify**: Ensure simplified code is functionally equivalent
35
36## Simplification Principles
37
381. **YAGNI**: Don't add abstractions until needed
392. **Preserve behavior**: Simplification ≠ changing functionality
403. **One change at a time**: Incremental is safer
414. **Readability over cleverness**: Clear beats "smart"
425. **Keep related code together**: Locality matters
43
44## Common Simplification Patterns
45
46### Extract and Name
47
48```python
49# Before: Complex inline condition
50if user.age >= 18 and user.country in ALLOWED and not user.banned:
51
52# After: Named condition
53is_eligible = user.age >= 18 and user.country in ALLOWED and not user.banned
54if is_eligible:
55```
56
57### Early Returns
58
59```python
60# Before: Deep nesting
61def process(data):
62 if data:
63 if data.valid:
64 if data.ready:
65 return compute(data)
66 return None
67
68# After: Guard clauses
69def process(data):
70 if not data or not data.valid or not data.ready:
71 return None
72 return compute(data)
73```
74
75### Comprehensions
76
77```python
78# Before: Manual loop
79result = []
80for item in items:
81 if item.active:
82 result.append(item.name)
83
84# After: List comprehension
85result = [item.name for item in items if item.active]
86```
87
88### Dictionary Techniques
89
90```python
91# Before: Verbose key checking
92if key in d:
93 value = d[key]
94else:
95 value = default
96
97# After: get() with default
98value = d.get(key, default)
99
100# Before: Manual grouping
101groups = {}
102for item in items:
103 if item.category not in groups:
104 groups[item.category] = []
105 groups[item.category].append(item)
106
107# After: defaultdict
108from collections import defaultdict
109groups = defaultdict(list)
110for item in items:
111 groups[item.category].append(item)
112```
113
114### Context Managers
115
116```python
117# Before: Manual cleanup
118f = open('file.txt')
119try:
120 data = f.read()
121finally:
122 f.close()
123
124# After: with statement
125with open('file.txt') as f:
126 data = f.read()
127```
128
129## Over-Engineering Anti-Patterns
130
131| Pattern | Problem | Solution |
132|---------|---------|----------|
133| Single-impl interface | Abstract class with one subclass | Merge or wait for need |
134| Unnecessary factory | Factory that creates one type | Direct instantiation |
135| Premature strategy | Strategy pattern with one strategy | Simple function |
136| Thin wrapper | Class that just delegates | Use wrapped class directly |
137| Speculative generality | Code for "future needs" | Delete it (YAGNI) |
138| Deep inheritance | 4+ levels of inheritance | Composition over inheritance |
139
140## Code Smells Quick Reference
141
142| Smell | Detection | Fix |
143|-------|-----------|-----|
144| Mutable default | `def f(x=[])` | Use `None`, create inside |
145| Bare except | `except:` | `except Exception:` |
146| God class | 15+ methods, 10+ attrs | Split into focused classes |
147| Long function | 50+ lines | Extract helper functions |
148| Deep nesting | 4+ levels | Early returns, extract |
149| Feature envy | Method uses other class more | Move method |
150| Magic numbers | Unexplained numeric literals | Named constants |
151
152## Script Reference
153
154| Script | What It Detects |
155|--------|-----------------|
156| `analyze_complexity.py` | Cyclomatic complexity, cognitive complexity, nesting depth, function length, parameter count, class size |
157| `find_code_smells.py` | Mutable defaults, bare excepts, magic numbers, type comparisons, god classes, data classes, boolean blindness |
158| `find_overengineering.py` | Single-implementation interfaces, unused abstractions, unnecessary factories/builders, thin wrappers, premature strategies |
159| `find_dead_code.py` | Unused imports, unused functions/classes, unused parameters, unreachable code, constant conditions |
160| `find_unpythonic.py` | `range(len())`, `== True/False/None`, swallowed exceptions, manual index tracking |
161| `find_coupling_issues.py` | Feature envy, low cohesion (LCOM), message chains, middle man classes |
162| `find_duplicates.py` | Structurally similar code blocks using AST normalization |
163
164## When NOT to Simplify
165
166- Working legacy code with no tests
167- Performance-critical hot paths (measure first)
168- Code that will be replaced soon
169- External API constraints requiring complexity