Pydantic: default_factory's result doesn't get deepcopy'd - bug or feature?

Created on 7 May 2020  路  2Comments  路  Source: samuelcolvin/pydantic

Bug

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

             pydantic version: 1.5.1
            pydantic compiled: False
                 install path: /home/johnc/.virtualenvs/myproject/lib/python3.7/site-packages/pydantic
               python version: 3.7.5 (default, Apr 19 2020, 20:18:17)  [GCC 9.2.1 20191008]
                     platform: Linux-5.3.0-51-generic-x86_64-with-Ubuntu-19.10-eoan
     optional deps. installed: []

Currently the result of default_factory isn't deepcopy'd, is this a bug or a feature?

In my use case it's actually useful, since my factory is intentionally returning a singleton, but I'm wondering if it's safe to rely on this?

If it's intended to stay this way I can add a unit test to avoid regression?
Or is this something you'd rather not lock down?

The relevant lines:https://github.com/samuelcolvin/pydantic/blob/5067508eca6222b9315b1e38a30ddc975dee05e7/pydantic/fields.py#L274-L282

Example code:

from typing import Union

import pydantic

class Sentinel:
  pass


MY_SENTINEL = Sentinel()

class Foo(pydantic.BaseModel):
  myfield: Union[str, Sentinel] = pydantic.Field(default_factory=lambda : MY_SENTINEL)

  class Config:
    arbitrary_types_allowed = True

assert Foo().myfield is Foo().myfield
bug

Most helpful comment

Hi @therefromhere!
It is indeed intentional ! This is the exact same behaviour as dataclasses from the standard library:

from dataclasses import dataclass, field
from typing import Union

class Sentinel:
  pass


MY_SENTINEL = Sentinel()

@dataclass
class Bar:
    myfield: Union[str, Sentinel] = field(default_factory=lambda: MY_SENTINEL)


assert Bar().myfield is Bar().myfield

All 2 comments

this is almost certainly intentional; having a customizable default_factory allows the user to provide unique copies if needed anyway

Hi @therefromhere!
It is indeed intentional ! This is the exact same behaviour as dataclasses from the standard library:

from dataclasses import dataclass, field
from typing import Union

class Sentinel:
  pass


MY_SENTINEL = Sentinel()

@dataclass
class Bar:
    myfield: Union[str, Sentinel] = field(default_factory=lambda: MY_SENTINEL)


assert Bar().myfield is Bar().myfield
Was this page helpful?
0 / 5 - 0 ratings