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

GenericModel - Recursive abstract class fails validation #2007

Closed
3 tasks done
choogeboom opened this issue Oct 14, 2020 · 2 comments · Fixed by #2549
Closed
3 tasks done

GenericModel - Recursive abstract class fails validation #2007

choogeboom opened this issue Oct 14, 2020 · 2 comments · Fixed by #2549
Labels
bug V1 Bug related to Pydantic V1.X

Comments

@choogeboom
Copy link
Contributor

Checks

  • I added a descriptive title to this issue
  • I have searched (google, github) for similar issues and couldn't find anything
  • I have read and followed the docs and still think this is a bug

Bug

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

             pydantic version: 1.6.1
            pydantic compiled: False
                 install path: /wayfair/home/ch438l/pydantic/pydantic
               python version: 3.6.8 (default, Aug 10 2019, 06:54:07)  [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
                     platform: Linux-3.10.0-862.3.2.el7.x86_64-x86_64-with-centos-7.5.1804-Core
     optional deps. installed: ['typing-extensions', 'email-validator', 'devtools']

If I try to define an attribute on a generic model to be a generic abstract class, then the model will fail to validate. Here's a failing test case:

@skip_36
def test_abstract_generic_type_recursion():
    T = TypeVar('T')

    class BaseInnerClass(GenericModel, abc.ABC, Generic[T]):
        base_data: T

        @abc.abstractmethod
        def base_abstract(self) -> None:
            pass

    class ConcreteInnerClass(BaseInnerClass[T], Generic[T]):

        def base_abstract(self) -> None:
            return None

    class OuterClass(GenericModel, Generic[T]):
        inner_class: BaseInnerClass[T]

    OuterClass[int](inner_class=ConcreteInnerClass[int](base_data=2))

It appears to be failing because the isinstance(value, cls) check is failing for the generic type.

@choogeboom choogeboom added the bug V1 Bug related to Pydantic V1.X label Oct 14, 2020
@choogeboom
Copy link
Contributor Author

OK, after some digging, I've figured out why this is happening. Due to how create_model is called in GenericModel.__class_getitem__, we have the following class relationships:

  • ConcreteInnerClass[int] < ConcreteInnerClass[T]
  • BaseInnerClass[int] < BaseInnerClass[T]
  • ConcreteInnerClass[T] < BaseInnerClass[T]

But we are missing the following:

  • ConcreteInnerClass[int] < BaseInnerClass[int]

@diabolo-dan
Copy link
Contributor

I've also been hitting this issue, and came to the same conclusion that the problem comes from the type hierarchy.

I think the correct fix is to include parameterised versions of subclasses in the hierarchy when constructing new classes.

I've constructed a PR to that effect, which should hopefully make it clear what I mean. #2549

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

Successfully merging a pull request may close this issue.

2 participants