I have a pydantic model like this:
class SomeModel(pydantic.BaseModel):
name: str
content: str
previous_model: typing.Optional["SomeModel"] = None
My code look like this, this is greatly simplified, In my real code there are many and circular dependencies occur by chance occasionally rather than being purposefully created:
models = [
SomeModel("bob", "2"),
SomeModel("bob", "2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]
models.remove(models[0])
This throws the following error:
File "c:\Users\username\project-name\src\main.py", line 108, in run_all
models.remove(models[0])
File "pydantic\main.py", line 902, in pydantic.main.BaseModel.__eq__
File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
File "pydantic\main.py", line 861, in _iter
File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
File "pydantic\main.py", line 861, in _iter
File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
File "pydantic\main.py", line 861, in _iter
File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
File "pydantic\main.py", line 861, in _iter
File "pydantic\main.py", line 736, in pydantic.main.BaseModel._get_value
File "pydantic\main.py", line 445, in pydantic.main.BaseModel.dict
File "pydantic\main.py", line 861, in _iter
... Snip several hunderd more lines
File "pydantic\main.py", line 734, in pydantic.main.BaseModel._get_value
File "pydantic\main.py", line 304, in pydantic.main.ModelMetaclass.__instancecheck__
RecursionError: maximum recursion depth exceeded
I don't really need the previous_model
field to be included in the equality at all. Is there a way to exclude it so that my stack doesn't overflow? This field is irrelevant for the purpose equality in this case.
CodePudding user response:
When pydantic generates __repr__
, it iterates over its arguments. That makes sense, that's one of the key selling points. But it doesn't work well in your scenario, you'd have to omit previous_node
from __repr__
to make it work. You can either skip previous_node
in __repr_args__
or return something simpler in __repr__
. To give you a very simplified example that is working
import typing
import pydantic
class SomeModel(pydantic.BaseModel):
name: str
content: str
previous_model: typing.Optional["SomeModel"] = None
def __repr__(self):
return self.name
SomeModel.update_forward_refs()
models = [
SomeModel(name="bob", content="2"),
SomeModel(name="bob", content="2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]
models.remove(models[0])
print(models)
Less simple version that's closer to how pydantic behaves but will also work in your case
import typing
import pydantic
class SomeModel(pydantic.BaseModel):
name: str
content: str
previous_model: typing.Optional["SomeModel"] = None
def __repr_args__(self, *args, **kwargs):
args = self.dict(exclude={'previous_model',})
return list(args.items())
SomeModel.update_forward_refs()
models = [
SomeModel(name="bob", content="2"),
SomeModel(name="bob", content="2"),
]
models[0].previous_model = models[1]
models[1].previous_model = models[0]
models.remove(models[0])