-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GenericModel[Any] raises ValidationError for GenericModel[SomeModel] #9414
Comments
Hi @gsakkis, Thanks for your questions. This comment might help to add some context regarding some of the nuances with validation and parametrized generics: #8884 (comment). The case with from typing import Any, Generic, TypeVar
from pydantic import BaseModel, ValidationError
T = TypeVar("T")
class ResponseModel(BaseModel, Generic[T]):
content: T
class Product(BaseModel):
name: str
price: float
class Order(BaseModel):
id: int
product: ResponseModel[Any]
# replacing the previous annotation with the following succeeds at runtime but fails type checking
# product: ResponseModel
product = Product(name="Apple", price=0.5)
response1: ResponseModel[Any] = ResponseModel[Any](content=product)
response2: ResponseModel[Any] = ResponseModel(content=product)
response3: ResponseModel[Any] = ResponseModel[Product](content=product)
for response in response1, response2, response3:
try:
order = Order(id=1, product=response.model_dump())
print(f"{response!r} succeeded")
except ValidationError:
print(f"{response!r} failed") I'm going to chat with @dmontagu, our resident generics expert, about this and will get back to you! |
So, the problem here is that we create concrete subclasses for each parametrization of a generic model, and model validation disallows instances of classes that are not subclasses. More concretely, let's consider The way I think this should work is that, any time you have a generic model which is an instance of a possibly-parametrized version of a subclass of the annotated field, if we would otherwise raise an error due to a violation of the class hierarchy, we should just attempt to revalidate the data similar-ish to how we would in v1. I'm not exactly sure of the exact semantics of how this should work, i.e., do we (effectively) call model_dump and model_validate, or try to re-validate the raw More specifically, let's say I have a type annotation Note that even if we did that, there is still some obvious misbehavior that I'm sure some people would complain about, and so we may want to change those semantics a bit. In particular, if if typing.TYPE_CHECKING:
ResponseModelAny = ResponseModel[Any]
else:
ResponseModelAny = ResponseModel but obviously that still kind of sucks. However, I still think this would be better for most common scenarios. There are also still other questions I don't have immediate answers for, such as — how does this interact with RootModels? How does it interact with subclasses of parametrized generic models (or partially parametrized generic models 😨)? How does it work with generic models that are subclasses of non-generic models? How does it work with subclasses of generic models that add more generic parameters? I won't be surprised if the answers to many of these questions end up being simple/obvious, but I will be surprised if that's the case for all of them. I'll also note that I think a better route for generics implementations long term will be to stop creating new classes for generics parametrizations, and instead have instances of a new subclass of There are just so many edge cases that it's hard to get the behavior right under all circumstances without functionally implementing a type-variance-aware runtime type checker, which I think is probably outside the scope of pydantic. But I do think we can chip away at these scenarios by tweaking the way model validation works in pydantic core, and just slowly expand the set of scenarios that don't raise errors, starting with revalidating data (e.g., by |
Initial Checks
Description
Annotating a field as
ResponseModel[Any]
(whereResponseModel
is a generic BaseModel) fails to validate values of typeResponseModel
andResponseModel[Product]
; it only validates values ofResponseModel[Any]
.On the other hand annotating the field as
ResponseModel
validates all three cases, however mypy givesMissing type parameters for generic type "ResponseModel"
.Questions:
ResponseModel[Any]
validation working as expected or it's a bug?# type: ignore
the second version that seems to be working?Example Code
Output:
Python, Pydantic & OS Version
The text was updated successfully, but these errors were encountered: