Skip to content
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

Type errors with @model_validator #8529

Closed
not-my-profile opened this issue Jan 10, 2024 · 5 comments · Fixed by #8639
Closed

Type errors with @model_validator #8529

not-my-profile opened this issue Jan 10, 2024 · 5 comments · Fixed by #8639

Comments

@not-my-profile
Copy link

not-my-profile commented Jan 10, 2024

The fix for #7152 was merged in v2.2.2 but apparently there has been a regression because with pydantic 2.5.3 pyright again reports a type error. It should be noted that this doesn't only happen for mode="wrap".

The same type error can also be observed with mode="before" and mode="after", for example the code from the documentation:

from typing import Any

from pydantic import BaseModel, ValidationError, model_validator


class UserModel(BaseModel):
    username: str
    password1: str
    password2: str

    @model_validator(mode='before')
    @classmethod
    def check_card_number_omitted(cls, data: Any) -> Any:
        if isinstance(data, dict):
            assert (
                'card_number' not in data
            ), 'card_number should not be included'
        return data

    @model_validator(mode='after')
    def check_passwords_match(self) -> 'UserModel':
        pw1 = self.password1
        pw2 = self.password2
        if pw1 is not None and pw2 is not None and pw1 != pw2:
            raise ValueError('passwords do not match')
        return self

results in the type errors:

/private/tmp/foo/foo.py
  /private/tmp/foo/foo.py:11:6 - error: Argument of type "(cls: type[Self@UserModel], data: Any) -> Any" cannot be assigned to parameter of type "_AnyModeBeforeValidator"
    Type "(cls: type[Self@UserModel], data: Any) -> Any" cannot be assigned to type "_AnyModeBeforeValidator"
      Type "(cls: type[Self@UserModel], data: Any) -> Any" cannot be assigned to type "(cls: Any, __value: Any, __info: ValidationInfo) -> Any"
        Function accepts too many positional parameters; expected 2 but received 3
          Parameter name mismatch: "__value" versus "data"
      Type "(cls: type[Self@UserModel], data: Any) -> Any" cannot be assigned to type "(cls: Any, __value: Any) -> Any"
        Parameter name mismatch: "__value" versus "data" (reportGeneralTypeIssues)
  /private/tmp/foo/foo.py:20:6 - error: Argument of type "(self: Self@UserModel) -> UserModel" cannot be assigned to parameter of type "_AnyModelAfterValidator[_ModelType@model_validator]"
    Type "(self: Self@UserModel) -> UserModel" cannot be assigned to type "_AnyModelAfterValidator[_ModelType@model_validator]"
      Type "(self: Self@UserModel) -> UserModel" cannot be assigned to type "(_ModelType@model_validator, ValidationInfo) -> _ModelType@model_validator"
        Function accepts too many positional parameters; expected 1 but received 2
          Parameter 1: type "_ModelType@model_validator" cannot be assigned to type "Self@UserModel"
            Type "UserModel" cannot be assigned to type "Self@UserModel"
      Type "(self: Self@UserModel) -> UserModel" cannot be assigned to type "(_ModelType@model_validator) -> _ModelType@model_validator"
        Parameter 1: type "_ModelType@model_validator" cannot be assigned to type "Self@UserModel"
          Type "UserModel" cannot be assigned to type "Self@UserModel" (reportGeneralTypeIssues)

Python, Pydantic & OS Version

pydantic version: 2.5.3
        pydantic-core version: 2.14.6
          pydantic-core build: profile=release pgo=true
                 install path: /private/tmp/foo/venv/lib/python3.11/site-packages/pydantic
               python version: 3.11.6 (main, Oct  2 2023, 13:45:54) [Clang 15.0.0 (clang-1500.0.40.1)]
                     platform: macOS-14.0-arm64-arm-64bit
             related packages: typing_extensions-4.9.0
@not-my-profile not-my-profile added bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation labels Jan 10, 2024
@sydney-runkle
Copy link
Member

@not-my-profile,

Thanks for bringing this to our attention. We're looking into this issue and a potential fix 👍.

@sydney-runkle sydney-runkle added question and removed bug V2 Bug related to Pydantic V2 pending Awaiting a response / confirmation labels Jan 10, 2024
@dmontagu
Copy link
Contributor

So, we have set up the model_validator as expecting the input function to return an instance of typing_extensions.Self. It's not 100% clear to me whether we should do this, but it does make sense that if model_validator requires an input that returns typing_extensions.Self, then using a specific class as the return type is not valid, even if our particular usage of model_validator couldn't actually result in errors caused by the incorrect typing.

At any rate, you should be able to eliminate this type error in your own code by setting the return type as typing_extensions.Self instead of 'UserModel' (which would actually be more narrow for subclasses anyway).

That said, it doesn't necessarily seem to me like we should put a restriction on the return type of a model_validator. It's been a while since I've looked deeply into the code though so there might be an assumption somewhere that you get a model as the output, if so then the current type hinting makes sense. I suspect it's an edge case to be returning something that isn't compatible with typing_extensions.Self as the return type of a model_validator, though I guess it could be annoying to be forced to use cls(...) everywhere (instead of MyType(...)).

If using typing_extensions.Self as the annotated return type doesn't work for you (or anyone else), I think an example showing why that's problematic would go a long way toward justifying a change to the type hints here.

And if we have examples like this in the docs it definitely makes sense to change the docs.

@not-my-profile
Copy link
Author

Sidenote: The type error for mode='before' appears to be different and apparently was addressed yesterday with #8479.

@noctuid
Copy link

noctuid commented Jan 25, 2024

At any rate, you should be able to eliminate this type error in your own code by setting the return type as typing_extensions.Self instead of 'UserModel' (which would actually be more narrow for subclasses anyway).

It works correctly (pyright no longer reports an error).

And if we have examples like this in the docs it definitely makes sense to change the docs.

Yes, the model_validator documentations uses -> "Model". This is just a documentation issue.

@sydney-runkle
Copy link
Member

@noctuid and @not-my-profile,

I just updated the docs with fixes for these type annotations. Thanks for bringing this to our attention :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants