Browse Source
🐛 Fix TYPE_CHECKING annotations for Python 3.14 (PEP 649) (#14789)
pull/14816/head
Mickaël Guérin
4 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with
46 additions and
5 deletions
-
fastapi/dependencies/utils.py
-
tests/test_stringified_annotation_dependency_py314.py
-
tests/test_tutorial/test_dependencies/test_tutorial008.py
-
tests/utils.py
|
|
|
@ -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 |
|
|
|
|
|
|
|
@ -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 |
|
|
|
@ -1,4 +1,5 @@ |
|
|
|
import importlib |
|
|
|
import sys |
|
|
|
from types import ModuleType |
|
|
|
from typing import Annotated, Any |
|
|
|
from unittest.mock import Mock, patch |
|
|
|
@ -12,8 +13,13 @@ from fastapi.testclient import TestClient |
|
|
|
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): |
|
|
|
|
|
|
|
@ -6,8 +6,8 @@ needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires pyth |
|
|
|
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+" |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|