python-code-patterns
Python code style and type hinting patterns. Use when writing or reviewing Python code to ensure consistent, modern type annotations and clean code structure. Covers modern type hints, import organization, comment practices, and docstring conventions.
When & Why to Use This Skill
This Claude skill streamlines Python development by enforcing modern coding standards, PEP 585 type hints, and clean architectural patterns. It solves the problem of inconsistent codebases by providing clear rules for import organization, docstring conventions, and the separation of business logic from API routers, ensuring high-quality, maintainable, and type-safe Python applications.
Use Cases
- Refactoring legacy Python codebases to adopt modern PEP 585 type hints and remove deprecated 'typing' module imports.
- Enforcing architectural integrity in FastAPI or Flask projects by migrating business logic from routers into dedicated service layers.
- Automating code reviews to identify and eliminate redundant comments, improper import guards, and unnecessary type-ignore flags.
- Standardizing project documentation by ensuring docstrings focus on logic and edge cases rather than repeating type information already present in signatures.
| name | python-code-patterns |
|---|---|
| description | Python code style and type hinting patterns. Use when writing or reviewing Python code to ensure consistent, modern type annotations, clean code structure, and proper separation of concerns. Covers modern type hints, import organization, router-service separation, comment practices, and docstring conventions. |
| author | eder |
| version | "1.2" |
Python Code Patterns
Exception: Tests Directory
All these rules can be relaxed in the tests/ directory. Go wild there - write whatever helps you test effectively.
Type Hints
Use modern Python type hints (PEP 585):
DO:
def process(items: list[str]) -> dict[str, int]:
...
data: list[dict[str, Any]] = []
```text
**DON'T:**
```python
from typing import List, Union, Optional
def process(items: List[str]) -> Dict[str, int]:
...
data: List[Dict[str, Any]] = []
Use lowercase built-in types: list, dict, tuple, set, frozenset, type, bytes
Import Organization
DO:
from typing import Any
def my_function(data: Any) -> None:
...
```text
**DON'T:**
```python
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any
def my_function(data: Any) -> None:
...
- Never use TYPE_CHECKING guards
- Never use imports not at the top level (inside functions, classes, etc.)
- If you have circular imports, your code structure needs refactoring
- TYPE_CHECKING guards and late imports are code smells indicating poor architecture
Type Checking
Avoid # type: ignore as much as possible:
- Only use as a last resort when there's a genuine limitation in type checking
- If you find yourself needing
# type: ignorefrequently, reconsider your type annotations - Comment why it's needed when you must use it:
result: int = some_complex_function() # type: ignore # external library has incorrect type stub
```text
## Comments
Don't add obvious/redundant comments:
**DON'T:**
```python
id: str # id
name: str # the name
count: int # number of items
# loop through items
for item in items:
# process the item
process(item)
Comments should explain why, not what.
Router-Service Separation
Keep routers free of business logic:
DO:
from fastapi import APIRouter
router = APIRouter()
@router.get("/items/{item_id}")
async def get_item(item_id: int):
return await service.get_item(item_id)
DON'T:
from fastapi import APIRouter
router = APIRouter()
@router.get("/items/{item_id}")
async def get_item(item_id: int):
if item_id <= 0:
raise HTTPException(status_code=400, detail="Invalid item ID")
item = db.query(Item).filter(Item.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
- Routers should only call service functions and return results
- Validation, checks, business logic belong in the service layer
- Services raise HTTPExceptions directly - let them bubble up through routers
- Services contain domain logic and return domain objects or primitives
Service layer handles:
- Input validation and sanitization
- Business rules and constraints
- Database operations and queries
- Error conditions and exceptions raising
Router layer handles:
- HTTP method routing
- Delegating to service functions
- Returning service results directly
- No business logic or validation
Docstrings
Keep docstrings concise and focused:
DON'T:
def process_data(data: list[str]) -> dict[str, int]:
"""
Process data.
Args:
data: The data to process
Returns:
dict[str, int]: The processed data
"""
...
```text
**DO:**
```python
def process_data(data: list[str]) -> dict[str, int]:
"""
Converts string data to word frequency counts, ignoring case and punctuation.
"""
...
- Type information already in signature - don't repeat it
- Only include non-obvious information
- Explain edge cases, constraints, algorithm choices, or complex behavior
When to Apply
Apply these patterns whenever:
- Writing new Python code (outside tests/)
- Reviewing or refactoring existing Python code (outside tests/)
- Updating type annotations (outside tests/)
- Adding or modifying imports (outside tests/)
- Implementing API routers or service layer functions