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

Subclass to a class inheriting from GenericModel - no validation at instantiation #1229

Closed
Esadruhn opened this issue Feb 13, 2020 · 7 comments
Labels
bug V1 Bug related to Pydantic V1.X

Comments

@Esadruhn
Copy link
Contributor

Bug

There is a bug, or maybe a feature request, with inheritance and Generics.

If I define a class that inherits from GenericModel and a subclass, then when I instanciate the subclass there is no check on the generic type.

From what I saw in the code, it is because the checks are done in the metaclass __new__ method (generics are treated as Optional[Any] at that point), and the generic type is defined afterwards, in the __getitem__ method.

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.4
            pydantic compiled: False
                 install path: $HOME/envs/thenv/lib/python3.7/site-packages/pydantic
               python version: 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21)  [Clang 6.0 (clang-600.0.57)]
                     platform: Darwin-19.3.0-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']

This test passes - no inheritance

def test_case_no_inheritance():
    TypeX = TypeVar('TypeX')

    SampleTypeX = list

    class MyBaseClass(GenericModel, Generic[TypeX]):
        X: TypeX

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        MyBaseClass[SampleTypeX](X=1)

Failed test case - inheritance

import pydantic
from pydantic.main import ModelMetaclass
from pydantic.generics import GenericModel
import pytest

def test_case():
    TypeX = TypeVar('TypeX')

    SampleTypeX = list

    class MyBaseClass(GenericModel, Generic[TypeX]):
        X: TypeX

    class ExampleMeta(ModelMetaclass):
        # This metaclass is not necessary for the test, it illustrates that we get the 'generic_type' in __getitem__
        def __getitem__(self, generic_type):
            return type('Example', (Example,), {'_my_types': (SampleTypeX, generic_type)})

    class Example(MyBaseClass[TypeX], metaclass=ExampleMeta):
        pass

    Example[SampleTypeX](X=[0, 1, 2])

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        Example[SampleTypeY](X=1)
@Esadruhn Esadruhn added the bug V1 Bug related to Pydantic V1.X label Feb 13, 2020
@samuelcolvin
Copy link
Member

humm, not sure. @dmontagu any idea whether we want to fix this?

@dmontagu
Copy link
Contributor

dmontagu commented Feb 18, 2020

It works all right if you repeat the Generic[TypeX] (and drop the metaclass):

from typing import TypeVar, Generic

import pydantic
from pydantic.main import ModelMetaclass
from pydantic.generics import GenericModel
import pytest

def test_case():
    TypeX = TypeVar('TypeX')

    SampleTypeX = list

    class MyBaseClass(GenericModel, Generic[TypeX]):
        X: TypeX

    class Example(MyBaseClass[TypeX], Generic[TypeX]):
        pass

    Example[SampleTypeX](X=[0, 1, 2])

    with pytest.raises(pydantic.error_wrappers.ValidationError):
        Example[SampleTypeX](X=1)

test_case()

@Esadruhn does this help?

I think it might make sense to refactor this more fully at some point for better compatibility with the typing library, though there are challenges with that as noted in other issues (namely, the typing package has been rather aggressive about keeping certain functionality private/internal use only).

@Esadruhn
Copy link
Contributor Author

Thank you for the answer, it works fine ! Pending a refactor, would it be possible to document this ?

@dmontagu
Copy link
Contributor

Yes, I think adding docs about it is a good idea. If you wanted to submit a PR adding the docs that would be awesome, otherwise I'll get to it eventually.

@Esadruhn
Copy link
Contributor Author

Esadruhn commented Feb 20, 2020

Thanks, I'll see if I can do it in the next few days.

I really like the inclusion of actual Python scripts in the doc !

make docs-serve works only with Python 3.8 as it has to build examples that use 3.8 features:

error in validation_decorator_parameter_types.py: Traceback (most recent call last):

pydantic/docs/examples/validation_decorator_parameter_types.py", line 20

`def pos_only(a: int, b: int = 2, /) -> str:  # python 3.8 only`

@samuelcolvin
Copy link
Member

Yes, that's expected, I should be able to build in a check that avoids executing that example for earlier versions.

@Esadruhn Esadruhn mentioned this issue Feb 21, 2020
4 tasks
@PrettyWood
Copy link
Member

Documentation added in #1249 to explain how to properly inherit from a GenericModel

RajatRajdeep pushed a commit to RajatRajdeep/pydantic that referenced this issue May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V1 Bug related to Pydantic V1.X
Projects
None yet
Development

No branches or pull requests

4 participants