From 454353954643e079c86a2bde57e75edcb512bb7b Mon Sep 17 00:00:00 2001 From: Nisar k Date: Mon, 18 May 2026 10:49:15 +0000 Subject: [PATCH] fix: support unresolved forward refs in annotated dependencies --- fastapi/dependencies/utils.py | 11 ++++ .../test_stringified_annotation_dependency.py | 60 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 7c6558c695..4812d2a15b 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -242,10 +242,21 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature: return typed_signature +class _DictWithForwardRefs(dict[str, Any]): + def __missing__(self, key: str) -> Any: + value = ForwardRef(key) + self[key] = value + return value + + def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any: if isinstance(annotation, str): + annotation_string = annotation annotation = ForwardRef(annotation) annotation = evaluate_forwardref(annotation, globalns, globalns) + if isinstance(annotation, ForwardRef) and "Annotated" in annotation_string: + annotationns = _DictWithForwardRefs(globalns) + annotation = eval(annotation_string, annotationns, annotationns) if annotation is type(None): return None return annotation diff --git a/tests/test_stringified_annotation_dependency.py b/tests/test_stringified_annotation_dependency.py index ce88074957..c75dad989e 100644 --- a/tests/test_stringified_annotation_dependency.py +++ b/tests/test_stringified_annotation_dependency.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass from typing import TYPE_CHECKING, Annotated import pytest @@ -27,6 +28,25 @@ async def get_client() -> AsyncGenerator[DummyClient, None]: Client = Annotated[DummyClient, Depends(get_client)] +app_with_late_forward_ref = FastAPI() + + +def get_potato() -> Potato: + return Potato(color="red", size=10) + + +@app_with_late_forward_ref.get("/") +async def get_late_forward_ref( + potato: Annotated[Potato, Depends(get_potato)], +) -> dict[str, str]: + return {"color": potato.color} + + +@dataclass +class Potato: + color: str + size: int + @pytest.fixture(name="client") def client_fixture() -> TestClient: @@ -77,3 +97,43 @@ def test_openapi_schema(client: TestClient): }, } ) + + +def test_late_forward_ref_dependency(): + client = TestClient(app_with_late_forward_ref) + + response = client.get("/") + + assert response.status_code == 200, response.text + assert response.json() == {"color": "red"} + + +def test_late_forward_ref_dependency_openapi(): + assert app_with_late_forward_ref.openapi() == snapshot( + { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/": { + "get": { + "summary": "Get Late Forward Ref", + "operationId": "get_late_forward_ref__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": {"type": "string"}, + "type": "object", + "title": "Response Get Late Forward Ref Get", + } + } + }, + } + }, + } + } + }, + } + )