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 no longer works with root models in V2 #6710

Closed
1 task done
MarkusSintonen opened this issue Jul 17, 2023 · 3 comments · Fixed by #9110
Closed
1 task done

arbitrary_types_allowed no longer works with root models in V2 #6710

MarkusSintonen opened this issue Jul 17, 2023 · 3 comments · Fixed by #9110
Labels
documentation help wanted Pull Request welcome

Comments

@MarkusSintonen
Copy link
Contributor

MarkusSintonen commented Jul 17, 2023

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

In V2 root models arbitrary_types_allowed no longer works. In V1 it used to work. Is it intended not to work anymore? Example below showing an use case that used to work in V1. I guess the issue is somehow related to generic models handling in V2.

Example Code

import numpy

from pydantic import BaseModel

try:
    from pydantic import ConfigDict, RootModel
    PYDANTIC_VERSION = "v2"
except ImportError:
    PYDANTIC_VERSION = "v1"


def test_arbitrary_root():
    if PYDANTIC_VERSION == 'v2':
        # Now it raises suggesting using arbitrary_types_allowed. But it is set!
        # Exception:
        # pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'numpy.ndarray'>.
        # Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.
        class MyNdarray(RootModel[numpy.ndarray]):
            model_config = ConfigDict(arbitrary_types_allowed=True)
            # defines deserialization of list to ndarray etc...
    else:
        # In v1 it works fine.
        class MyNdarray(BaseModel):
            __root__: numpy.ndarray

            class Config:
                arbitrary_types_allowed = True

            # defines deserialization of list to ndarray etc...

Python, Pydantic & OS Version

pydantic version: 2.0.3
        pydantic-core version: 2.3.0 release build profile
                 install path: /Users/markussintonen/Library/Caches/pypoetry/virtualenvs/leak-test-VvUvdXxD-py3.10/lib/python3.10/site-packages/pydantic
               python version: 3.10.12 (main, Jun 20 2023, 17:00:24) [Clang 14.0.3 (clang-1403.0.22.14.1)]
                     platform: macOS-13.4.1-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']

Selected Assignee: @lig

@MarkusSintonen MarkusSintonen added bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable labels Jul 17, 2023
@dmontagu
Copy link
Contributor

dmontagu commented Jul 17, 2023

@MarkusSintonen while I think ideally this wouldn't error, I think it might be hard to get it to work properly, since it's erroring while trying to create RootModel[np.ndarray], which you are inheriting from. Instead, you can do:

from typing import Any

import numpy

from pydantic import ConfigDict, RootModel


class MyNdarray(RootModel[Any]):
    root: numpy.ndarray
    model_config = ConfigDict(arbitrary_types_allowed=True)
    # defines deserialization of list to ndarray etc...

I was worried this might not give the right mypy behavior, but I wasn't able to produce any typing issues using the above, as far as I could tell it behaved the way you'd want it to (same as RootModel[numpy.ndarray], just doesn't give the arbitrary types error). (Let us know if you discover issues with it though.)

I'd be open to PRs fixing it for RootModel but I don't think the right answer is to make arbitrary_types_allowed=True for RootModel by default, and I don't see another good way to achieve this.

Open to debate/other ideas

@lig
Copy link
Contributor

lig commented Jul 17, 2023

@dmontagu I think that both forms should work identical in general despite what type is being used for RootModel.

class MyNdarray(RootModel[numpy.ndarray]):
    model_config = ConfigDict(arbitrary_types_allowed=True)

and

class MyNdarray(RootModel):
    root = numpy.ndarray
    model_config = ConfigDict(arbitrary_types_allowed=True)

I agree that arbitrary_types_allowed shouldn't be True by default.

I will focus in this issue just on making arbitrary_types_allowed=True work as expected.

@lig lig added help wanted Pull Request welcome documentation and removed unconfirmed Bug not yet confirmed as valid/applicable bug V2 Bug related to Pydantic V2 labels Aug 31, 2023
@lig
Copy link
Contributor

lig commented Aug 31, 2023

After thorough review of this issue I'm tempting to call this known behavior and add a note regarding this to the docs.

The issue here is that RootModel[numpy.ndarray] creates a specialized subclass of RootModel that could be used as a full functional model already. Two code examples below produce mostly identical results.

class Model(RootModel):
    root: numpy.ndarray 

and

Model = RootModel[numpy.ndarray]

When creating a RootModel subclass explicitly we can set model_config for it and when creating using an implicit subclass as in RootModel[numpy.ndarray] we cannot.

This means that in the code below we are attempting to create a RootModel subclass without model_config defined before setting model_config for MyNdarray.

class MyNdarray(RootModel[numpy.ndarray]):
    model_config = ConfigDict(arbitrary_types_allowed=True)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation help wanted Pull Request welcome
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants