Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion fastapi/dependencies/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,12 @@ def _get_signature(call: Callable[..., Any]) -> inspect.Signature:
except NameError:
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI
# e.g. dependency return types
signature = inspect.signature(call)
if sys.version_info >= (3, 14):
from annotationlib import Format

signature = inspect.signature(call, annotation_format=Format.FORWARDREF)
else:
signature = inspect.signature(call)
else:
signature = inspect.signature(call)
return signature
Expand Down
30 changes: 30 additions & 0 deletions tests/test_stringified_annotation_dependency_py314.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import TYPE_CHECKING, Annotated

from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient

from .utils import needs_py314

if TYPE_CHECKING: # pragma: no cover

class DummyUser: ...


@needs_py314
def test_stringified_annotation():
# python3.14: Use forward reference without "from __future__ import annotations"
async def get_current_user() -> DummyUser | None:
return None

app = FastAPI()

client = TestClient(app)

@app.get("/")
async def get(
current_user: Annotated[DummyUser | None, Depends(get_current_user)],
) -> str:
return "hello world"

response = client.get("/")
assert response.status_code == 200
10 changes: 8 additions & 2 deletions tests/test_tutorial/test_dependencies/test_tutorial008.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import importlib
import sys
from types import ModuleType
from typing import Annotated, Any
from unittest.mock import Mock, patch
Expand All @@ -12,8 +13,13 @@
name="module",
params=[
"tutorial008_py39",
# Fails with `NameError: name 'DepA' is not defined`
pytest.param("tutorial008_an_py39", marks=pytest.mark.xfail),
pytest.param(
"tutorial008_an_py39",
marks=pytest.mark.xfail(
sys.version_info < (3, 14),
reason="Fails with `NameError: name 'DepA' is not defined`",
),
),
],
)
def get_module(request: pytest.FixtureRequest):
Expand Down
4 changes: 2 additions & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
needs_py310 = pytest.mark.skipif(
sys.version_info < (3, 10), reason="requires python3.10+"
)
needs_py_lt_314 = pytest.mark.skipif(
sys.version_info >= (3, 14), reason="requires python3.13-"
needs_py314 = pytest.mark.skipif(
sys.version_info < (3, 14), reason="requires python3.14+"
)


Expand Down