| name | python-code-review |
| description | Reviews Python code for PEP8 compliance, type hints, modern best practices (Python 3.9+), and code quality. Detects anti-patterns, validates documentation, and provides severity-based recommendations. Integrates with modern tooling (ruff, mypy, uv). |
Python Code Review Skill
A specialized skill for reviewing Python code with focus on PEP8 compliance, type hints, modern best practices, and code quality.
Overview
This skill enables Claude to autonomously review Python code for compliance with modern Python standards, including PEP8 style guidelines, type hint coverage, proper structure, and common anti-patterns. It promotes clean, maintainable, and type-safe Python code.
Capabilities
When activated, this skill provides:
PEP8 Compliance Review
- Check naming conventions (snake_case, PascalCase)
- Verify line length (88 characters)
- Review whitespace and indentation
- Check import ordering and grouping
- Validate docstring presence and format
Type Hints Validation
- Ensure all functions have type hints
- Check for proper return type annotations
- Validate use of modern type syntax (list[], dict[] over List[], Dict[])
- Verify Optional[] vs None union types
- Check for Any types (discourage overuse)
Code Structure & Organization
- Review module structure and imports
- Check for proper
__init__.pyusage - Validate entry points (
__main__.py) - Review function/class organization
- Assess module coupling and cohesion
Best Practices Enforcement
- Check for use of context managers (with statements)
- Validate exception handling
- Review string formatting (f-strings preferred)
- Check for proper constant definitions
- Assess use of comprehensions vs loops
Common Anti-Patterns Detection
- Mutable default arguments
- Bare except clauses
- Using
globalkeyword unnecessarily - Poor variable naming
- Overly complex functions (high cyclomatic complexity)
Documentation Quality
- Check for docstrings on public functions/classes
- Validate docstring format (Google/NumPy style)
- Review parameter documentation
- Check for return value documentation
- Assess example usage in docstrings
Usage
This skill activates automatically when:
- User requests: "Review this Python code"
- User asks: "Check PEP8 compliance"
- User mentions: "Validate type hints"
- User wants: "Improve this Python code"
- Code review is requested for .py files
- User asks: "Is this code following best practices?"
Review Approach
1. Initial Assessment
- Identify Python version being used
- Understand the code's purpose
- Determine if it's application code or library code
- Check project structure context
2. PEP8 Compliance Check
Naming Conventions:
- ✅ Functions/variables:
snake_case - ✅ Classes:
PascalCase - ✅ Constants:
UPPER_SNAKE_CASE - ✅ Private members:
_leading_underscore - ✅ Module names:
lowercaseorsnake_case
Code Layout:
- ✅ Line length: 88 characters (Black standard)
- ✅ Indentation: 4 spaces
- ✅ Blank lines: 2 before top-level definitions, 1 between methods
- ✅ Import order: stdlib → third-party → local
- ✅ One import per line (except from imports)
3. Type Hints Review
Check for:
# ❌ Missing type hints
def process(data):
return data.upper()
# ✅ Proper type hints
def process(data: str) -> str:
return data.upper()
# ❌ Old-style typing (Python <3.9)
from typing import List, Dict
def get_items() -> List[Dict[str, int]]:
pass
# ✅ Modern type syntax (Python 3.9+)
def get_items() -> list[dict[str, int]]:
pass
# ❌ Overuse of Any
from typing import Any
def process(data: Any) -> Any:
pass
# ✅ Specific types
def process(data: str | int) -> str:
return str(data)
4. Common Issues Detection
Mutable Default Arguments:
# ❌ Dangerous
def append_to(element: str, target: list[str] = []) -> list[str]:
target.append(element)
return target
# ✅ Safe
def append_to(element: str, target: list[str] | None = None) -> list[str]:
if target is None:
target = []
target.append(element)
return target
Exception Handling:
# ❌ Bare except
try:
risky_operation()
except:
pass
# ✅ Specific exception
try:
risky_operation()
except ValueError as e:
logger.error(f"Invalid value: {e}")
raise
String Formatting:
# ❌ Old style
name = "World"
print("Hello %s" % name)
print("Hello {}".format(name))
# ✅ Modern f-strings
print(f"Hello {name}")
Context Managers:
# ❌ Manual resource management
file = open("data.txt")
data = file.read()
file.close()
# ✅ Context manager
with open("data.txt") as file:
data = file.read()
5. Code Quality Assessment
Function Complexity:
- Functions should do one thing well
- Aim for <20 lines per function
- Cyclomatic complexity should be low (<10)
- Deep nesting (>3 levels) indicates need for refactoring
Example - Complex to Simple:
# ❌ Too complex
def process_user(user):
if user:
if user.active:
if user.email:
if "@" in user.email:
return user.email.lower()
return None
# ✅ Simplified
def process_user(user: User | None) -> str | None:
if not user or not user.active or not user.email:
return None
if "@" not in user.email:
return None
return user.email.lower()
Output Format
Provide review results in this structured format:
## Python Code Review Results
### Summary
[Brief overview of the code quality and main findings]
### PEP8 Compliance: [PASS/FAIL/PARTIAL]
**Issues Found:**
- Line 15: Line too long (102 characters, limit 88)
- Line 23: Missing blank line after function definition
- Line 45: Import should be at top of file
### Type Hints Coverage: [X%]
**Missing Type Hints:**
- Line 10: Function `process_data` missing return type
- Line 18: Parameter `config` missing type annotation
- Line 25: Using deprecated `List` instead of `list`
**Recommendations:**
```python
# Current
def process_data(items, limit=10):
return items[:limit]
# Improved
def process_data(items: list[str], limit: int = 10) -> list[str]:
return items[:limit]
Code Quality Issues
Critical 🔴
- Line 34: Mutable default argument in
add_item(item, items=[]) - Line 56: Bare except clause catching all exceptions
Important 🟡
- Line 12: Function complexity too high (15 branches)
- Line 78: Using
globalkeyword - consider refactoring - Line 91: Variable name
xis not descriptive
Minor 🔵
- Line 23: Could use f-string instead of .format()
- Line 45: List comprehension would be more Pythonic
Best Practices Violations
Line 15-18: Manual file handling
- Current:
f = open("data.txt") data = f.read() f.close()- Improved:
with open("data.txt") as f: data = f.read()Line 34: Mutable default argument
- Impact: Can cause bugs when function is called multiple times
- Fix: Use None as default, create new list inside function
Positive Aspects ✅
- Consistent naming conventions throughout
- Good use of type hints in 80% of functions
- Proper docstrings on all public methods
- Clean import organization
Documentation Review
- ✅ All public functions have docstrings
- ✅ Docstrings follow Google style format
- ⚠️ Some docstrings missing return value description
- ⚠️ No examples in docstrings (consider adding for complex functions)
Modernization Suggestions
- Update to Python 3.10+ syntax (use
|for unions instead ofUnion) - Consider using
match/casestatement (Python 3.10+) for complex conditionals - Replace
Dict,Listwithdict,list(Python 3.9+)
Next Steps
- Fix critical issues (mutable defaults, bare excepts)
- Add missing type hints
- Refactor complex functions (>15 lines)
- Run automated tools:
uv run ruff check . # Linting uv run mypy . # Type checking uv run ruff format . # Auto-formatting
Recommended Tools
- ruff: Fast linting and formatting (replaces black, flake8, isort)
- mypy: Static type checking
- pytest: Testing framework
- coverage: Code coverage analysis
Overall Assessment: [GOOD/NEEDS IMPROVEMENT/EXCELLENT]
[Brief summary and priority recommendations]
## Review Standards
### PEP8 Checklist
**Naming:**
- [ ] Functions and variables use snake_case
- [ ] Classes use PascalCase
- [ ] Constants use UPPER_SNAKE_CASE
- [ ] Private members start with underscore
**Formatting:**
- [ ] Line length ≤ 88 characters
- [ ] 4 spaces for indentation (no tabs)
- [ ] 2 blank lines before class/function definitions
- [ ] 1 blank line between methods
**Imports:**
- [ ] Grouped: stdlib → third-party → local
- [ ] Alphabetically sorted within groups
- [ ] No wildcard imports (`from module import *`)
- [ ] Unused imports removed
### Type Hints Standards
**Required:**
```python
# All function parameters
def func(name: str, age: int) -> None:
pass
# Return types (even None)
def get_data() -> dict[str, Any]:
pass
# Class attributes
class User:
name: str
age: int
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
Modern Syntax (Python 3.9+):
- Use
list[]notList[] - Use
dict[]notDict[] - Use
tuple[]notTuple[] - Use
set[]notSet[] - Use
|notUnion[](Python 3.10+)
Documentation Standards
Google Style Docstrings:
def function_name(param1: str, param2: int) -> bool:
"""Brief description of function.
Longer description explaining what the function does,
its behavior, and any important details.
Args:
param1: Description of param1
param2: Description of param2
Returns:
Description of return value
Raises:
ValueError: When param2 is negative
Example:
>>> function_name("test", 5)
True
"""
pass
Best Practices to Promote
- Use type hints everywhere - Improves IDE support and catches bugs early
- Prefer f-strings - More readable than format() or %
- Use context managers - For file handling, locks, connections
- Write small functions - Each function should do one thing well
- Use comprehensions - More Pythonic than loops for simple transformations
- Avoid global state - Pass dependencies explicitly
- Use dataclasses - For data containers (Python 3.7+)
- Use pathlib - Instead of os.path for file operations
- Use enums - For fixed sets of constants
- Write docstrings - For all public APIs
Common Refactoring Patterns
Replace Loop with Comprehension
# ❌ Before
result = []
for item in items:
if item.active:
result.append(item.name)
# ✅ After
result = [item.name for item in items if item.active]
Use Dataclass
# ❌ Before
class User:
def __init__(self, name, email, age):
self.name = name
self.email = email
self.age = age
# ✅ After
from dataclasses import dataclass
@dataclass
class User:
name: str
email: str
age: int
Use Pathlib
# ❌ Before
import os
path = os.path.join("data", "file.txt")
if os.path.exists(path):
with open(path) as f:
data = f.read()
# ✅ After
from pathlib import Path
path = Path("data") / "file.txt"
if path.exists():
data = path.read_text()
Integration
This skill works seamlessly with:
- Code review workflows
- Pull request reviews
- Refactoring tasks
- Code quality improvement projects
- Pre-commit validation
- Migration to modern Python versions
- Type hint adoption projects