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

arbitrary_types_allowed not respected when validating stdlib dataclasses #2054

Closed
3 tasks done
ines opened this issue Oct 27, 2020 · 6 comments · Fixed by #2051
Closed
3 tasks done

arbitrary_types_allowed not respected when validating stdlib dataclasses #2054

ines opened this issue Oct 27, 2020 · 6 comments · Fixed by #2051

Comments

@ines
Copy link
Contributor

ines commented Oct 27, 2020

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.7
            pydantic compiled: True
                 install path: /Users/ines/Repos/explosion/spacy/.env37/lib/python3.7/site-packages/pydantic
               python version: 3.7.2 (default, Oct  6 2019, 23:51:55)  [Clang 10.0.1 (clang-1001.0.46.4)]
                     platform: Darwin-19.5.0-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']

As of v1.7, Pydantic validates dataclasses and their contents, but it fails if the dataclass specifies arbitrary types (which is pretty common in our code bases). Here's an example:

from typing import List
from dataclasses import dataclass
from pydantic import BaseModel

class ArbitraryType:
    def __init__(self):
        ...

@dataclass
class Test:
    foo: ArbitraryType
    bar: List[ArbitraryType]


class TestModel(BaseModel):
    a: ArbitraryType  # this is fine
    b: Test  # this raises RuntimeError

    class Config:
        arbitrary_types_allowed = True

Even though the BaseModel subclass sets arbitrary_types_allowed to True, this configuration doesn't seem to be taken into account when validating the dataclass fields.

Adding custom validation to our dataclasses isn't really an option, since we wouldn't want those Pydantic specifics to leak into the rest of the code base. I'm also wondering whether there should be an option to not validate stdlib dataclasses as Pydantic dataclasses and just use a basic instance check instead, like it previously did (?) before v1.7?

@ines ines added the bug V1 Bug related to Pydantic V1.X label Oct 27, 2020
@samuelcolvin samuelcolvin added feature request and removed bug V1 Bug related to Pydantic V1.X labels Oct 27, 2020
@samuelcolvin
Copy link
Member

I had this same problem yesterday.

it's not a bug since it matches the behaviour of models or dataclasses in the past. In this situation a model didn't know where it was going to be used when it was created (which is when this check takes place), so couldn't respect arbitrary_types_allowed.

I think it might be possible to find a work around for this, but it might not be trivial. @PrettyWood do you have any idea how hard it would be to respect arbitrary_types_allowed of the parent model? I guess in theory we can get access to config in the validator, so it shouldn't be hard hard.

Also, are there any other config attributes we should respect too? I guess not.

@tiangolo
Copy link
Member

I think the main issue in this specific example is that it is using the standard lib dataclasses.dataclass instead of Pydantic's pydantic.dataclasses.dataclass.

So, here Test would be indeed an "arbitrary type", not a Pydantic model (I think).


Extending the example from @ines :

from typing import List
from dataclasses import dataclass
from pydantic import BaseModel


class ArbitraryType:
    def __init__(self, name: str):
        self.name = name


@dataclass
class Test:
    foo: ArbitraryType
    bar: List[ArbitraryType]


class TestModel(BaseModel):
    a: ArbitraryType  # this is fine
    b: Test  # this raises RuntimeError

    class Config:
        arbitrary_types_allowed = True


foo = ArbitraryType(name='Foo')
bar = [ArbitraryType(name='Bar'), ArbitraryType(name='Baz')]
test = Test(foo=foo, bar=bar)
test_model = TestModel(a=ArbitraryType(name='A'), b=test)


assert test_model.a.name == 'A'
assert test_model.b.bar[1].name == 'Baz'

Running this script with Pydantic < 1.7 passes. But raises in 1.7.

But interestingly, in Pydantic < 1.7, changing the line:

from dataclasses import dataclass

to

from pydantic.dataclasses import dataclass

then makes it raise the same way as it is raising now.


I feel like this could be related to @PrettyWood 's fix at #2051, but I tried locally with the code from his PR and sadly it didn't fix this specific case.

@samuelcolvin
Copy link
Member

It's because prior to 1.7 you couldn't use dataclasses as field types (well you could, but you had to add arbitrary_types_allowed and there were interpreted like any other arbitrary type).

With 1.7, pydantic will inspect the dataclass and do full validation on the dataclass fields, but problem is when the dataclass as unknown field types, the step of converting the stardard library dataclass to a pydantic dataclass doesn't respect config on the outer model.

PrettyWood added a commit to PrettyWood/pydantic that referenced this issue Oct 27, 2020
@PrettyWood
Copy link
Member

The fix is quite easy in fact. I added it in #2051 86aaff2

@samuelcolvin
Copy link
Member

that's awesome. Thank you.

@tiangolo
Copy link
Member

With 1.7, pydantic will inspect the dataclass and do full validation on the dataclass fields, but problem is when the dataclass as unknown field types, the step of converting the stardard library dataclass to a pydantic dataclass doesn't respect config on the outer model.

Thanks for the clarification @samuelcolvin ! I didn't know that was possible now, very cool! 🚀 🎉

And thanks @PrettyWood , you rock! 🎸

samuelcolvin pushed a commit that referenced this issue Oct 28, 2020
…ary_types_allowed` is supported (#2051)

* fix: pydantic dataclasses can inherit from stdlib dataclasses

closes #2042

* docs: add some documentation

* fix: support arbitrary_types_allowed with stdlib dataclass

closes #2054

* docs: add documentation for custom types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants