Compare commits

...

3 Commits

Author SHA1 Message Date
Sebastián Ramírez
7b0b915749 🔖 Release version 0.124.2 2025-12-10 13:07:53 +01:00
github-actions[bot]
96bdde376f 📝 Update release notes
[skip ci]
2025-12-10 12:06:32 +00:00
Sebastián Ramírez
7ba042e069 🐛 Fix support for if TYPE_CHECKING, non-evaluated stringified annotations (#14485) 2025-12-10 13:06:05 +01:00
4 changed files with 100 additions and 7 deletions

View File

@@ -7,6 +7,12 @@ hide:
## Latest Changes
## 0.124.2
### Fixes
* 🐛 Fix support for `if TYPE_CHECKING`, non-evaluated stringified annotations. PR [#14485](https://github.com/fastapi/fastapi/pull/14485) by [@tiangolo](https://github.com/tiangolo).
## 0.124.1
### Fixes

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.124.1"
__version__ = "0.124.2"
from starlette import status as status

View File

@@ -209,11 +209,21 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
return path_params + query_params + header_params + cookie_params
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
def _get_signature(call: Callable[..., Any]) -> inspect.Signature:
if sys.version_info >= (3, 10):
signature = inspect.signature(call, eval_str=True)
try:
signature = inspect.signature(call, eval_str=True)
except NameError:
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI
# e.g. dependency return types
signature = inspect.signature(call)
else:
signature = inspect.signature(call)
return signature
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = _get_signature(call)
unwrapped = inspect.unwrap(call)
globalns = getattr(unwrapped, "__globals__", {})
typed_params = [
@@ -239,10 +249,7 @@ def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
if sys.version_info >= (3, 10):
signature = inspect.signature(call, eval_str=True)
else:
signature = inspect.signature(call)
signature = _get_signature(call)
unwrapped = inspect.unwrap(call)
annotation = signature.return_annotation

View File

@@ -0,0 +1,80 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from typing_extensions import Annotated
if TYPE_CHECKING: # pragma: no cover
from collections.abc import AsyncGenerator
class DummyClient:
async def get_people(self) -> list:
return ["John Doe", "Jane Doe"]
async def close(self) -> None:
pass
async def get_client() -> AsyncGenerator[DummyClient, None]:
client = DummyClient()
yield client
await client.close()
Client = Annotated[DummyClient, Depends(get_client)]
@pytest.fixture(name="client")
def client_fixture() -> TestClient:
app = FastAPI()
@app.get("/")
async def get_people(client: Client) -> list:
return await client.get_people()
client = TestClient(app)
return client
def test_get(client: TestClient):
response = client.get("/")
assert response.status_code == 200, response.text
assert response.json() == ["John Doe", "Jane Doe"]
def test_openapi_schema(client: TestClient):
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == snapshot(
{
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/": {
"get": {
"summary": "Get People",
"operationId": "get_people__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"items": {},
"type": "array",
"title": "Response Get People Get",
}
}
},
}
},
}
}
},
}
)