fastapi: RecursionError from response model in 0.47.1

Describe the bug

FastAPI 0.47.1 will not be able to start due to a RecursionError when there is a circular reference among models. The issue seems to originate from https://github.com/tiangolo/fastapi/pull/889. This works fine in 0.46.0.

Environment

  • OS: Windows
  • FastAPI Version: 0.47.1
  • Python version: 3.7.0

To Reproduce

from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel, Field


class Group(BaseModel):
    representative: Optional['Person'] = Field(None)


class Person(BaseModel):
    group: Optional[Group] = Field(None)


Group.update_forward_refs()


app = FastAPI()


@app.get('/group/{group_id}', response_model=Group)
def get_group(group_id):
    return []

Expected behavior

No exception

Actual output

Traceback (most recent call last):
  File "test.py", line 21, in <module>
    @app.get('/group/{group_id}', response_model=Group)
  File "D:\virtualenvs\test\lib\site-packages\fastapi\routing.py", line 494, in decorator
    callbacks=callbacks,
  File "D:\virtualenvs\test\lib\site-packages\fastapi\routing.py", line 438, in add_api_route
    callbacks=callbacks,
  File "D:\virtualenvs\test\lib\site-packages\fastapi\routing.py", line 275, in __init__
    ] = create_cloned_field(self.response_field)
  File "D:\virtualenvs\test\lib\site-packages\fastapi\utils.py", line 100, in create_cloned_field
    use_type.__fields__[f.name] = create_cloned_field(f)
  File "D:\virtualenvs\test\lib\site-packages\fastapi\utils.py", line 100, in create_cloned_field
    use_type.__fields__[f.name] = create_cloned_field(f)
  File "D:\virtualenvs\test\lib\site-packages\fastapi\utils.py", line 100, in create_cloned_field
    use_type.__fields__[f.name] = create_cloned_field(f)
  [Previous line repeated 981 more times]
  File "D:\virtualenvs\test\lib\site-packages\fastapi\utils.py", line 97, in create_cloned_field
    original_type.__name__, __config__=original_type.__config__
  File "D:\virtualenvs\test\lib\site-packages\pydantic\main.py", line 773, in create_model
    return type(model_name, (__base__,), namespace)
  File "D:\virtualenvs\test\lib\site-packages\pydantic\main.py", line 152, in __new__
    if issubclass(base, BaseModel) and base != BaseModel:
  File "D:\virtualenvs\test\lib\abc.py", line 143, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
RecursionError: maximum recursion depth exceeded in comparison

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 6
  • Comments: 17 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Thanks for the discussion here everyone!

This was fixed by @voegtlel in https://github.com/tiangolo/fastapi/pull/1164 🚀 🎉

It will be available in the next release (today in a couple of hours).

I’ll re-open this issue to give @ysmu a chance to confirm it’s fixed and close it.

Ah thanks for fixing this, our front-end team really appreciates it 😉

Fix works for me. Thank you!

Hi there, I’ve suggested another MR to fix this issue. The one of @mateuszz0000 was missing the recursive field resolution. EDIT: Coverage showed me I was wrong here and used an older version of fastapi for my initial test of the fix 🙈 Works without that part (so removed it from the commit).

Still, I think local cloning (i.e. keeping dicts of what was cloned) should be preferred before setting another hidden attribute?

I’ve been busy so haven’t had a lot of time to look into this just yet.

@zamiramir that’s actually very useful information – I suspect it is related to the cythonization of pydantic.

Any chance you could run from pydantic.version import version_info; print(version_info()) in both environments (MacOS and non-MacOS), and share the output?

Sorry just tested it again, and got the same issue on MacOS. Might have been an older version of FastAPI.