Pydantic: External dict-like type gets mapped to dict (unwanted behaviour)

Created on 2 Feb 2021  Â·  2Comments  Â·  Source: samuelcolvin/pydantic

Checks

  • [x] I added a descriptive title to this issue
  • [x] I have searched (google, github) for similar issues and couldn't find anything
  • [x] I have read and followed the docs and couldn't find an answer
  • [x] After submitting this, I commit to one of:

    • Look through open issues and helped at least one other person

    • Hit the "watch" button on this repo to receive notifications and I commit to help at least 2 people that ask questions in the future

    • Implement a Pull Request for a confirmed bug

Question

I'm trying to instantiate a class inheriting from BaseModel that has one member variable. I want this to be an immutable mapping type; currently, I'm using FrozenDict from flax while waiting for PEP 603.

The problem is that pydantic seems to recognise this type as a dict, and tries to cast to a dict as stated in the docs when typing.Dict is used for a field. This works regardless of setting arbitrary_types_allowed to True or False. Is there any way I can circumvent this behaviour in this one case, while still having the isinstance check I would expect for arbitrary_types_allowed = True?

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

             pydantic version: 1.7.3
            pydantic compiled: True
                 install path: /Users/phinate/.venvs/clarinet/lib/python3.8/site-packages/pydantic
               python version: 3.8.3 (default, May 19 2020, 13:54:14)  [Clang 10.0.0 ]
                     platform: macOS-10.15.7-x86_64-i386-64bit
     optional deps. installed: []
from pydantic import BaseModel
from flax.core import FrozenDict, freeze
​
class Example(BaseModel):
    dct: FrozenDict[str, int]
​
d = freeze(dict(x=3))
type(d)
> flax.core.frozen_dict.FrozenDict

class_d = Example(dct=d).dct
type(class_d)
> dict

...
question

All 2 comments

Hi @phinate!

Yeah it's currently expected from _pydantic_. I guess we could try to keep the original type by adding

    elif issubclass(type(v), Mapping):
        return type(v)(result), None

here https://github.com/samuelcolvin/pydantic/blob/bd9c5723c676395363689549268738153e45a7e5/pydantic/fields.py#L737-L740

In the meantime the easiest workaround is to add a validator

class Example(BaseModel):
    dct: FrozenDict[str, int]

    @validator('dct')
    def to_frozendict(cls, v):
        return FrozenDict(v)

Hope it helps!

Thanks, this works great!

I'd say from a user perspective, it would be nice to not have implicit casting (you could lose access to class methods, for example), but that's ultimately your decision.

Oh and thanks for working on pydantic, its pretty awesome :)

Was this page helpful?
0 / 5 - 0 ratings