Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pydantic cannot pickle private attribute

I tried to set a private attribute (that cannot be pickled) to my model:

from threading import Lock
from pydantic import BaseModel

class MyModel(BaseModel):
    class Config:
        underscore_attrs_are_private = True

    _lock: Lock = Lock() # This cannot be copied

x = MyModel()

But this produces an error:

Traceback (most recent call last):
  File ".../example.py", line 9, in <module>
    x = MyModel()
  File "pydantic\main.py", line 349, in pydantic.main.BaseModel.__init__
  File "pydantic\main.py", line 419, in pydantic.main.BaseModel._init_private_attributes
  File "pydantic\fields.py", line 1180, in pydantic.fields.ModelPrivateAttr.get_default
  File "pydantic\utils.py", line 657, in pydantic.utils.smart_deepcopy
  File "...\lib\copy.py", line 161, in deepcopy
    rv = reductor(4)
TypeError: cannot pickle '_thread.lock' object

It seems it fails because Lock cannot be pickled (or copied). Furthermore, it seems Pydantic tries to copy private attributes for some reason. I looked in the docs and could not find a model property to override this. Also, the configs arbitrary_types_allowed or copy_on_model_validation have no effect. I also tried to use PrivateAttr(default=Lock()) but that did not help.

Interestingly, the example works if the attribute was not private (by setting underscore_attrs_are_private = False in Config).

I would like to have this attribute as private. How can I set a private attribute that cannot be pickled to Pydantic Model?

like image 953
miksus Avatar asked Jul 19 '26 14:07

miksus


1 Answers

I found the answer myself after doing some more investigation. It seems this can be solved using default_factory:

from threading import Lock
from pydantic import BaseModel, PrivateAttr

class MyModel(BaseModel):
    class Config:
        underscore_attrs_are_private = True
        
    _lock = PrivateAttr(default_factory=Lock)
    
x = MyModel()

Furthermore, it seems the copying is done in the BaseModel init only. One can also set the attribute later:

from threading import Lock
from pydantic import BaseModel, PrivateAttr

class MyModel(BaseModel):
    class Config:
        underscore_attrs_are_private = True
        
    _lock: Lock
        
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._lock = Lock()
    
x = MyModel()
like image 142
miksus Avatar answered Jul 22 '26 02:07

miksus