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
V2: Descriminated Unions #4675
Comments
Would it be alright for myself and @marcoo47 to request taking this one over as first time contributors? If so, what would you recommend for first steps, we've been reading over the repository code and documentation in the manual. |
Absolutely, have a go. You might also want to read through pydantic-core and understand how new descriminated unions work. I'll give some code examples of the final interface soon. |
Thank you so much! |
Hi, my partner and I have read through most of the relevant documentation. Did you ever make the code examples for the final interface you mentioned? |
Interface should be as follows: from typing import Literal, Union, Annotated
from pydantic import BaseModel, Field
from pydantic.descriminators import Tag
class Cat(BaseModel):
meows: int
class Dog(BaseModel):
barks: float
class Lizard(BaseModel):
scales: bool
class Model(BaseModel):
pet: Union[
Annotated[Cat, Tag('cat')],
Annotated[Dog, Tag('dog')],
Annotated[Lizard, Tag('lizard')],
] = Field(..., discriminator='pet_type')
n: int A few notes:
|
I was wondering how you would like paths and functions to work for discriminated unions. Would they be like the variables being used to determine class like above (a function within the list of classes/objects to be decided from) or an outside function that is run to determine the object instead? |
I don't understand, it's either a string, a list, or a function. Shouldn't matter to your logic. |
Alright, I understand after double checking the schema. |
Are there any example cases of the discriminator being a path or function? |
Yes lots in the tests. |
We've looked at the tests, use cases, examples, and other guides for discriminated unions. I think we misinterpreted the initial issue as needing to implement discriminated-unions with path and function cases but it looks like that's already done in pydantic core based on the attached test case. I guess we're just unsure about what your expectations were from our contribution. |
Yes, it's all implemented in pydantic-core. This issue, and most issues in pydantic related to V2 about about supporting the functionality in pydantic, using type hints, then updating and extending the unit tests to work. I think everything in |
Hi @samuelcolvin I'd like to take this over. I think I have a decent idea of how to start and can open a PR in the next few days. |
Great, go for it. |
From the blog and #4883 (comment), it seems like def test_discriminated_union_root_same_discriminator():
class BlackCat(BaseModel):
pet_type: Literal['blackcat']
class WhiteCat(BaseModel):
pet_type: Literal['whitecat']
class Cat(BaseModel):
__root__: Union[BlackCat, WhiteCat]
class Dog(BaseModel):
pet_type: Literal['dog']
with pytest.raises(PydanticUserError, match="Field 'pet_type' is not the same for all submodels of 'Cat'"):
class Pet(BaseModel):
__root__: Union[Cat, Dog] = Field(..., discriminator='pet_type') |
hey @samuelcolvin ping on the above question! I have most of it figured out, but I'm a bit blocked on how you were envisioning replacing |
You're right, In that specific case, you could just replace Cat = Union[BlackCat, WhiteCat] and everything should work as expected. For the general case of replacing So a simplified variant of your example would become from pydantic import BaseModel, validate
class Cat(BaseModel):
pet_type: Literal['cat']
class Dog(BaseModel):
pet_type: Literal['dig']
Pet = Cat | Dog
ValidatedPet = validate(Pet) The question then is: what is |
to be clear, this doesn't really have anything to do with discriminated unions, except that I guess |
Slight delay here, but I've made some progress with roughly half the tests passing. Some questions:
|
Here's a draft PR with my progress so far. Depending on the answers to my questions above, I think I might need to make some edits to |
Thanks so much @philhchen for digging into this. These seem like really insightful questions.
Good point, I guess we'll need to change the behaviour of the tagged union validator in pydantic-core to assume
Again good catch, you'll need to change:
There might be some more nuance, e.g. for other discriminator types, but let's start with the obvious choices. If you can create a PR (or two PRs) for this on pydantic-core that would be amazing, otherwise I can look into it. |
Thanks for the help! Is there a reason to use |
Descriminated unions are getting lots of super powers in V2, probably best example right now is the tests in pydantic-core, see https://github.com/pydantic/pydantic-core/blob/main/tests/validators/test_tagged_union.py.
In particular, descriminated union desciminators can now be a path or a function.
How should we define these? I guess it can all be done with
Field
andAnnotated
?The text was updated successfully, but these errors were encountered: