Python Typing
Choose the strongest valid lane automatically. Do not ask the user to pick a typing philosophy.
Consult references/typing-policy.md for the full policy document.
Required Policy
- Forbid
Any, broad object, and unchecked cast() in normal internal code
- Allow them only at explicit boundaries where unknown-shape data enters
- Isolate boundary code in dedicated validator, parser, adapter, or boundary modules
- Validate immediately and return strongly typed internal objects
- Do not let raw payloads cross into the typed core
- Allow narrow lint exceptions for
Any only in approved boundary modules
Lane Selection
1. Python 3.10 Constrained or stdlib-only
- Use only compatibility-safe stdlib typing features
- Prefer
dataclasses, TypedDict, Protocol, Literal, TypeGuard, NewType
- Validate with explicit runtime checks in dedicated boundary wrappers
- No third-party type assumptions
2. Python 3.11+ stdlib-only
- Use modern stdlib typing features supported by the interpreter
- Use
Self, assert_type, and reveal_type where useful during refactoring
TypedDict with NotRequired
3. Python 3.11+ with Pydantic
- Use Pydantic models for ingress contracts
- Prefer strict mode where coercion would hide upstream errors
- Use
TypeAdapter for annotated types that do not need full models
- See
references/pydantic-boundaries.md
4. Python 3.11+ with Hypothesis
- Property-test boundaries, validators, parsers, and invariants
- Prefer
from_type() where practical
- See
references/hypothesis-boundaries.md
5. Python 3.12+
- Use
type statement for explicit type aliases: type JSONValue = str | int | ...
- Use PEP 695 generic parameter syntax for new generic helpers
6. Python 3.13+
- Use
TypeIs for clearer custom narrowing helpers (replaces TypeGuard where bidirectional narrowing needed)
- Use
ReadOnly in TypedDict fields that must not mutate after validation
7. Python 3.14+
- Keep annotation-reading infrastructure compatible with deferred evaluation (PEP 649)
- Use
annotationlib.get_annotations() in infrastructure that inspects annotations at runtime
Boundary Implementation Standard
Use dedicated wrappers named like:
parse_*
validate_*
decode_*
coerce_*
*_from_raw
Boundary modules should return typed objects only.
Example: stdlib-only boundary
from typing import TypedDict, NotRequired
from dataclasses import dataclass
class _RawIncoming(TypedDict):
user_id: int
email: str
metadata: NotRequired[dict[str, str]]
@dataclass(frozen=True, slots=True)
class IncomingPayload:
user_id: int
email: str
metadata: dict[str, str]
def parse_incoming(data: _RawIncoming) -> IncomingPayload:
return IncomingPayload(user_id=data["user_id"], email=data["email"], metadata=data.get("metadata", {}))
Example: Pydantic boundary
from pydantic import BaseModel, TypeAdapter
class IncomingPayload(BaseModel):
user_id: int
email: str
metadata: dict[str, str] = {}
model_config = {"strict": True}
def parse_incoming(data: dict[str, object]) -> IncomingPayload:
return IncomingPayload.model_validate(data)
References
references/typing-policy.md — full boundary validation policy
references/pydantic-boundaries.md — Pydantic model and TypeAdapter patterns
references/hypothesis-boundaries.md — property-based testing for validators
1---2name: python3-typing3description: Auto-selects and enforces the strongest valid Python typing lane for the detected Python version and dependencies — no user input required. Use when adding or tightening type annotations, eliminating Any usage in internal code, designing boundary validators or parsers, choosing between stdlib typing (TypedDict, Protocol, dataclasses), Pydantic models, or Hypothesis property tests, addressing ty or mypy failures, or applying version-specific features (TypeIs, ReadOnly, PEP 695 generics, PEP 649 deferred evaluation). Enforces boundary isolation — raw payloads validated immediately at ingress and returned as typed internal objects.4user-invocable: false5---67# Python Typing89Choose the strongest valid lane automatically. Do not ask the user to pick a typing philosophy.1011Consult `references/typing-policy.md` for the full policy document.1213## Required Policy1415- Forbid `Any`, broad `object`, and unchecked `cast()` in normal internal code16- Allow them only at explicit boundaries where unknown-shape data enters17- Isolate boundary code in dedicated validator, parser, adapter, or boundary modules18- Validate immediately and return strongly typed internal objects19- Do not let raw payloads cross into the typed core20- Allow narrow lint exceptions for `Any` only in approved boundary modules2122## Lane Selection2324### 1. Python 3.10 Constrained or stdlib-only2526- Use only compatibility-safe stdlib typing features27- Prefer `dataclasses`, `TypedDict`, `Protocol`, `Literal`, `TypeGuard`, `NewType`28- Validate with explicit runtime checks in dedicated boundary wrappers29- No third-party type assumptions3031### 2. Python 3.11+ stdlib-only3233- Use modern stdlib typing features supported by the interpreter34- Use `Self`, `assert_type`, and `reveal_type` where useful during refactoring35- `TypedDict` with `NotRequired`3637### 3. Python 3.11+ with Pydantic3839- Use Pydantic models for ingress contracts40- Prefer strict mode where coercion would hide upstream errors41- Use `TypeAdapter` for annotated types that do not need full models42- See `references/pydantic-boundaries.md`4344### 4. Python 3.11+ with Hypothesis4546- Property-test boundaries, validators, parsers, and invariants47- Prefer `from_type()` where practical48- See `references/hypothesis-boundaries.md`4950### 5. Python 3.12+5152- Use `type` statement for explicit type aliases: `type JSONValue = str | int | ...`53- Use PEP 695 generic parameter syntax for new generic helpers5455### 6. Python 3.13+5657- Use `TypeIs` for clearer custom narrowing helpers (replaces `TypeGuard` where bidirectional narrowing needed)58- Use `ReadOnly` in `TypedDict` fields that must not mutate after validation5960### 7. Python 3.14+6162- Keep annotation-reading infrastructure compatible with deferred evaluation (PEP 649)63- Use `annotationlib.get_annotations()` in infrastructure that inspects annotations at runtime6465## Boundary Implementation Standard6667Use dedicated wrappers named like:6869- `parse_*`70- `validate_*`71- `decode_*`72- `coerce_*`73- `*_from_raw`7475Boundary modules should return typed objects only.7677### Example: stdlib-only boundary7879```python80from typing import TypedDict, NotRequired81from dataclasses import dataclass828384class _RawIncoming(TypedDict):85 user_id: int86 email: str87 metadata: NotRequired[dict[str, str]]888990@dataclass(frozen=True, slots=True)91class IncomingPayload:92 user_id: int93 email: str94 metadata: dict[str, str]959697def parse_incoming(data: _RawIncoming) -> IncomingPayload:98 return IncomingPayload(user_id=data["user_id"], email=data["email"], metadata=data.get("metadata", {}))99```100101### Example: Pydantic boundary102103```python104from pydantic import BaseModel, TypeAdapter105106107class IncomingPayload(BaseModel):108 user_id: int109 email: str110 metadata: dict[str, str] = {}111 model_config = {"strict": True}112113114def parse_incoming(data: dict[str, object]) -> IncomingPayload:115 return IncomingPayload.model_validate(data)116```117118## References119120- `references/typing-policy.md` — full boundary validation policy121- `references/pydantic-boundaries.md` — Pydantic model and TypeAdapter patterns122- `references/hypothesis-boundaries.md` — property-based testing for validators