pydantic: field defined with `Field()` makes pydantic to have a "RecursionError: maximum recursion depth exceeded"
Initial Checks
- I confirm that I’m using Pydantic V2
Description
pydantic 2.x leads to a recursion error when running the example code.
(....)
File "/home/luminoso/PycharmProjects/pydantic2datamodel-bug-report/venv/lib/python3.11/site-packages/pydantic/_internal/_repr.py", line 55, in __repr_str__
return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded
to test, try to parse an object:
Model.parse_obj({"Settings": {"name": "foobar"}})
(Despite pydantic 2 deprecated parse_obj()
the problem also happens with pydantic v2 model_validate()
)
A workaround is not to use the Field()
, as so:
class Model(BaseModel):
Settings: Settings
Where the problem doesn’t happen.
And the solution is to downgrade pydantic 1.10.12
, where the problem does not occur with both with or without the Field()
definition.
Example Code
from __future__ import annotations
from pydantic.fields import Field
from pydantic.main import BaseModel
class Settings(BaseModel):
name: str
class Model(BaseModel):
Settings: Settings = Field(..., title="a nice title")
Python, Pydantic & OS Version
pydantic version: 2.3.0
pydantic-core version: 2.6.3
pydantic-core build: profile=release pgo=true
install path: /var/home/luminoso/PycharmProjects/pydantic2datamodel-bug-report/venv/lib/python3.11/site-packages/pydantic
python version: 3.11.4 (main, Jun 7 2023, 00:00:00) [GCC 13.1.1 20230511 (Red Hat 13.1.1-2)]
platform: Linux-6.1.50-200.fc38.x86_64-x86_64-with-glibc2.37
optional deps. installed: ['email-validator', 'typing-extensions']
Selected Assignee: @dmontagu
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Reactions: 1
- Comments: 17 (7 by maintainers)
Commits related to this issue
- Resolve https://github.com/pydantic/pydantic/issues/7327 — committed to sneakers-the-rat/pydantic by sneakers-the-rat 10 months ago
any reason why this issue is closed? 😦
It only works with
from __future__ import annotations
. When I said it has to do with how Python finds references it might be slightly incorrect: Pydantic defines a lot of extra utilities to determine what type hints have been used. So this might differ from Pydantic v1 where it could be able to determine the type correctly when type hints are evaluated as strings thanks to the future import.While I don’t think supporting this use case with or without the future import (I don’t know if it is even possible without this import) is a good idea, a better error message could be a good improvement.
For this use case, an alias can be used, or reference the
Settings
class under a different name (not pretty):I played with this a bit, and it looks like it comes down to the fact that when
from __future__ import annotations
is present, the namespace of the class is ignored when usingtyping.get_type_hints
, whereas when it is not present, it is not ignored.I’m not sure the details here, so maybe it is fixable. I thought I was onto something by removing the
localns = dict(vars(base))
line frompydantic._internal._typing_extra.get_cls_type_hints_lenient
, which doesn’t break any tests except one designed to demonstrate this exact misbehavior. But I realized that removing that actually makes it stop matching the logic for resolving annotations when you don’t havefrom __future__ import annotations
:(Note there is nothing specific to pydantic above, it’s all standard library only.)
So, unless there is a way to determine whether
from __future__ import annotations
was imported in the module in which the class was defined, I don’t see a way to make the change that is undeniably an improvement. (And even if you could determine that, it is arguably still a breaking change to the current behavior that would likely be painful to resolve if you were affected.)Given all of this, I think the best course of action is to just treat this as an unfortunately unfixable “misbehavior”, at least until PEP649 is live (at which point I think the improvements that will bring would merit making the arguably-breaking change to always reflect the semantics of that PEP).
For what it’s worth, if you don’t have another workaround available, you might also find the approach from
tests.test_edge_cases.test_invalid_forward_ref_model
useful as a way to work around collisions between field names and type names.Just following up since the prior issue was closed - is this an impossible to fix bug? Or would it be possible to at least have an informative error message saying you cant have a field with the same name as the type?
It seems odd it only happens when you have an explicit Field object and not otherwise, but idk anything about the implementation that might explain that