Python Code Review with Modern Typing
Review Python code ensuring strict type safety with Python 3.12+ conventions. Use when reviewing Python code, writing new Python code, or refactoring existing Python code.
$ Installieren
git clone https://github.com/jack-michaud/faire /tmp/faire && cp -r /tmp/faire/jack-software/skills/code-review/python-code-review-types ~/.claude/skills/faire// tip: Run this command in your terminal to install the skill
name: Python Code Review with Modern Typing description: Review Python code ensuring strict type safety with Python 3.12+ conventions. Use when reviewing Python code, writing new Python code, or refactoring existing Python code.
Python Code Review with Modern Typing
Overview
This skill provides systematic Python code review with emphasis on Python 3.12+ type annotations. It enforces strict typing and built-in generic types (list, dict, type) instead of deprecated typing equivalents (List, Dict, Type).
Scope: This skill focuses exclusively on type annotations, not code logic, design patterns, performance, or general quality.
When to Use
- Reviewing pull requests containing Python code
- Writing new Python functions, classes, or modules
- Refactoring existing Python code to modern standards
- Conducting code audits for type safety
- Before merging Python code to main branch
Process
Focus only on type annotation issues.
1. Type Annotation Audit
Check every function, method, and variable for proper type annotations:
# â
CORRECT - Modern Python 3.12 syntax
def process_items(items: list[str], count: int) -> dict[str, int]:
result: dict[str, int] = {}
for item in items[:count]:
result[item] = len(item)
return result
# â INCORRECT - Missing types
def process_items(items, count):
result = {}
for item in items[:count]:
result[item] = len(item)
return result
# â INCORRECT - Old typing module syntax
from typing import List, Dict
def process_items(items: List[str], count: int) -> Dict[str, int]:
...
Required checks:
- All function parameters have type annotations
- All function return types are annotated (use
-> Noneif no return) - All class attributes have type annotations
- Complex variables have inline type annotations
- No imports from
typingforList,Dict,Set,Tuple,Type
2. Built-in Generic Types
Verify usage of built-in generics instead of typing module equivalents:
Use these (Python 3.12+):
list[T]notList[T]dict[K, V]notDict[K, V]set[T]notSet[T]tuple[T, ...]notTuple[T, ...]type[T]notType[T]
Import from typing:
Any,Optional,Union,Callable,Protocol,TypeVar,GenericLiteral,TypedDict,NotRequired,Requiredoverload,final,override
# â
CORRECT - Modern syntax
from typing import Protocol, TypeVar
T = TypeVar('T')
class Container(Protocol):
def get_items(self) -> list[str]: ...
def merge_dicts(a: dict[str, int], b: dict[str, int]) -> dict[str, int]:
return {**a, **b}
# â INCORRECT - Old typing module imports
from typing import List, Dict, Protocol
def merge_dicts(a: Dict[str, int], b: Dict[str, int]) -> Dict[str, int]:
return {**a, **b}
3. Optional and Union Types
Check for proper use of modern union syntax:
# â
CORRECT - Python 3.10+ union syntax
def find_user(user_id: int) -> dict[str, str] | None:
return users.get(user_id)
def process(value: int | str | float) -> str:
return str(value)
# â INCORRECT - Old Optional/Union syntax
from typing import Optional, Union
def find_user(user_id: int) -> Optional[dict[str, str]]:
return users.get(user_id)
def process(value: Union[int, str, float]) -> str:
return str(value)
4. Type Completeness Check
Ensure no untyped code exists:
Check these locations:
- Function signatures (parameters and returns)
- Lambda expressions (where possible)
- Class attributes and properties
- Module-level variables
- Generator and comprehension expressions (when complex)
# â
CORRECT - Fully typed
class UserService:
_cache: dict[int, str]
def __init__(self, cache: dict[int, str] | None = None) -> None:
self._cache = cache or {}
def get_user(self, user_id: int) -> str | None:
return self._cache.get(user_id)
# â INCORRECT - Untyped attribute
class UserService:
def __init__(self, cache=None):
self._cache = cache or {}
def get_user(self, user_id):
return self._cache.get(user_id)
5. Type Checking Validation
Run static type checker:
mypy --strict your_module.py
Configure in pyproject.toml:
[tool.mypy]
strict = true
python_version = "3.12"
6. Review Checklist
- No imports of
List,Dict,Set,Tuple,Typefromtyping - All functions have parameter and return type annotations
- All class attributes are typed
- Union types use
|syntax, notUnion[] - Optional types use
X | None, notOptional[X] - Complex nested types are properly annotated
-
mypy --strictpasses without errors - No
# type: ignorecomments without justification
Examples
Example 1: API Handler Review
Before (â):
from typing import Dict, List, Optional
def get_users(limit=10, offset=0):
users = fetch_from_db(limit, offset)
return {"users": users, "count": len(users)}
def create_user(data):
user_id = save_to_db(data)
return {"id": user_id}
After (â ):
from typing import TypedDict
class UserResponse(TypedDict):
users: list[dict[str, str]]
count: int
class CreateResponse(TypedDict):
id: int
def get_users(limit: int = 10, offset: int = 0) -> UserResponse:
users: list[dict[str, str]] = fetch_from_db(limit, offset)
return {"users": users, "count": len(users)}
def create_user(data: dict[str, str]) -> CreateResponse:
user_id: int = save_to_db(data)
return {"id": user_id}
Example 2: Data Processing Pipeline
Before (â):
from typing import List, Dict, Callable
def transform_data(data, transformers):
result = []
for item in data:
for transformer in transformers:
item = transformer(item)
result.append(item)
return result
After (â ):
from typing import Callable, TypeVar
T = TypeVar('T')
def transform_data(
data: list[T],
transformers: list[Callable[[T], T]]
) -> list[T]:
result: list[T] = []
for item in data:
for transformer in transformers:
item = transformer(item)
result.append(item)
return result
Example 3: Class with Type Parameters
Before (â):
from typing import Generic, TypeVar, List, Optional
T = TypeVar('T')
class Container(Generic[T]):
def __init__(self):
self._items = []
def add(self, item):
self._items.append(item)
def get_all(self):
return self._items
After (â ):
from typing import Generic, TypeVar
T = TypeVar('T')
class Container(Generic[T]):
_items: list[T]
def __init__(self) -> None:
self._items = []
def add(self, item: T) -> None:
self._items.append(item)
def get_all(self) -> list[T]:
return self._items
Anti-patterns
-
â Don't: Import
List,Dict,Set,Tuple,Typefromtypingmodule- â
Do: Use built-in
list,dict,set,tuple,typewith generic syntax
- â
Do: Use built-in
-
â Don't: Use
Optional[X]orUnion[X, Y]syntax- â
Do: Use
X | NoneandX | Yunion syntax
- â
Do: Use
-
â Don't: Leave any function, method, or class attribute untyped
- â Do: Add complete type annotations everywhere
-
â Don't: Use
# type: ignorewithout explanation- â Do: Fix the type issue or document why it's necessary
-
â Don't: Use
Anyas a shortcut to avoid thinking about types- â Do: Define proper types even if they're complex
-
â Don't: Skip type annotations on simple functions
- â Do: Type everything, even trivial functions
-
â Don't: Mix old and new typing syntax in the same codebase
- â Do: Consistently use Python 3.12+ syntax throughout
Testing This Skill
Follow instructions in examples/EXAMPLES.md.
Remember: No untyped code. Use Python 3.12+ built-in generics. Type everything.
Repository
