yanyongyu / githubkit

The modern, all-batteries-included GitHub SDK for Python, including rest api, graphql, webhooks, like octokit!
https://yanyongyu.github.io/githubkit/
MIT License
200 stars 25 forks source link

Can't validate UNSET from json dumped model #165

Open alexdrydew opened 3 hours ago

alexdrydew commented 3 hours ago

Pydantic V2 does not allow validating Literal of plain enum by enum values:

from githubkit.typing import Missing
from githubkit.compat import GitHubModel
from githubkit.utils import UNSET

class TestModel(GitHubModel):
    a: Missing[bool] = UNSET

TestModel.model_validate(TestModel().model_dump(mode="json"))

raises

ValidationError: 2 validation errors for TestModel
a.literal[<UNSET>]
  Input should be <UNSET> [type=literal_error, input_value='<UNSET>', input_type=str]
    For further information visit https://errors.pydantic.dev/2.8/v/literal_error
a.bool
  Input should be a valid boolean, unable to interpret input [type=bool_parsing, input_value='<UNSET>', input_type=str]
    For further information visit https://errors.pydantic.dev/2.8/v/bool_parsing

Relevant pydantic issue: https://github.com/pydantic/pydantic/issues/8708

Pydantic and python version:

pydantic version: 2.8.2
        pydantic-core version: 2.20.1
          pydantic-core build: profile=release pgo=truepackages/pydantic)
               python version: 3.10.14 (main, Apr 15 2024, 18:40:16) [Clang 17.0.6 ]
                     platform: macOS-15.0.1-arm64-arm-64bit
             related packages: typing_extensions-4.12.2 mypy-1.11.2 fastapi-0.109.1 pydantic-settings-2.0.3
                       commit: unknown
yanyongyu commented 3 hours ago

Ops... githubkit has already added a custom validation function for the Unset type. This seems not working.

yanyongyu commented 3 hours ago

Hi, i just reproduce the example. To convert a model into a jsonable dict (json mode), you should use:

from githubkit.utils import UNSET
from githubkit.typing import Missing
from githubkit.compat import GitHubModel

class TestModel(GitHubModel):
    a: Missing[bool] = UNSET

a = TestModel().model_dump(mode="json", exclude_unset=True)
TestModel.model_validate(a)

Since UNSET is just a placeholder and should not be converted into string type (which is a valid data type), you need to turn on the exclude_unset option.

alexdrydew commented 3 hours ago

Oh, right, that works. Thank you! Just a suggestion: maybe it would be handy to use a custom serializer not to include UNSET by default when serializing models?

yanyongyu commented 2 hours ago

adding a serializer will make the pydantic v1/v2 compat layer too complex. i could add a note to the documentation.