-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Summary
The runtime coercion from #106 works great, but mypy still complains about list invariance because the type hints use list[Product] instead of something covariant.
Problem
from adcp import GetProductsResponse, Product
class OurProduct(Product):
internal_config: dict | None = Field(default=None, exclude=True)
products: list[OurProduct] = [OurProduct(id="1", name="Test")]
# Runtime: works (BeforeValidator coercion)
# Mypy: error - list[OurProduct] not assignable to list[Product]
response = GetProductsResponse(products=products)We currently work around this with cast():
response = GetProductsResponse(products=cast(list[Product], products))Potential Solutions
Option 1: Use Union in type hints
from typing import Union
class GetProductsResponse(BaseModel):
# Accept list of Product or any subclass
products: list[Product] | list[Any]This is ugly and loses type safety.
Option 2: Use Sequence (covariant) in public API
from typing import Sequence
class GetProductsResponse(BaseModel):
# Sequence is covariant, so Sequence[SubClass] is assignable
products: Sequence[Product]Issue: Pydantic may not handle Sequence the same as list.
Option 3: Type stub overrides
Provide .pyi stub files that declare the fields as accepting subclass lists, while the actual implementation uses list.
Option 4: Custom generic types
from typing import TypeVar, Generic
T = TypeVar("T", bound=Product)
class GetProductsResponse(BaseModel, Generic[T]):
products: list[T]Allows GetProductsResponse[OurProduct] but changes the API.
Option 5: Accept current limitation
Document that cast() is needed for mypy when using subclass lists, since this is a fundamental Python typing limitation (list invariance).
Recommendation
I'd lean toward Option 5 (document the limitation) or Option 2 if Pydantic handles Sequence properly. The BeforeValidator handles runtime coercion, and cast() is a reasonable workaround for static typing since it has no runtime cost.
Would love to hear thoughts on whether there's a cleaner solution I'm missing.
Related
- PR feat: extend type ergonomics to response types #106 - Runtime coercion for response types
- PR feat: improve type ergonomics for library consumers #103 - Runtime coercion for request types
🤖 Generated with Claude Code