Merge branch 'master' into feat/enhanced-json-decode-errors

This commit is contained in:
Motov Yurii 2026-02-04 17:29:02 +03:00 committed by GitHub
commit 2fbf6f31fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 7 deletions

View File

@ -14,11 +14,13 @@ hide:
### Fixes ### Fixes
* 🐛 Fix TYPE_CHECKING annotations for Python 3.14 (PEP 649). PR [#14789](https://github.com/fastapi/fastapi/pull/14789) by [@mgu](https://github.com/mgu).
* 🐛 Strip whitespaces from `Authorization` header credentials. PR [#14786](https://github.com/fastapi/fastapi/pull/14786) by [@WaveTheory1](https://github.com/WaveTheory1). * 🐛 Strip whitespaces from `Authorization` header credentials. PR [#14786](https://github.com/fastapi/fastapi/pull/14786) by [@WaveTheory1](https://github.com/WaveTheory1).
* 🐛 Fix OpenAPI duplication of `anyOf` refs for app-level responses with specified `content` and `model` as `Union`. PR [#14463](https://github.com/fastapi/fastapi/pull/14463) by [@DJMcoder](https://github.com/DJMcoder). * 🐛 Fix OpenAPI duplication of `anyOf` refs for app-level responses with specified `content` and `model` as `Union`. PR [#14463](https://github.com/fastapi/fastapi/pull/14463) by [@DJMcoder](https://github.com/DJMcoder).
### Refactors ### Refactors
* 🎨 Tweak types for mypy. PR [#14816](https://github.com/fastapi/fastapi/pull/14816) by [@tiangolo](https://github.com/tiangolo).
* 🏷️ Re-export `IncEx` type from Pydantic instead of duplicating it. PR [#14641](https://github.com/fastapi/fastapi/pull/14641) by [@mvanderlee](https://github.com/mvanderlee). * 🏷️ Re-export `IncEx` type from Pydantic instead of duplicating it. PR [#14641](https://github.com/fastapi/fastapi/pull/14641) by [@mvanderlee](https://github.com/mvanderlee).
* 💡 Update comment for Pydantic internals. PR [#14814](https://github.com/fastapi/fastapi/pull/14814) by [@tiangolo](https://github.com/tiangolo). * 💡 Update comment for Pydantic internals. PR [#14814](https://github.com/fastapi/fastapi/pull/14814) by [@tiangolo](https://github.com/tiangolo).

View File

@ -204,7 +204,12 @@ def _get_signature(call: Callable[..., Any]) -> inspect.Signature:
except NameError: except NameError:
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI # Handle type annotations with if TYPE_CHECKING, not used by FastAPI
# e.g. dependency return types # 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: else:
signature = inspect.signature(call) signature = inspect.signature(call)
return signature return signature

View File

@ -219,9 +219,9 @@ def jsonable_encoder(
if isinstance(obj, encoder_type): if isinstance(obj, encoder_type):
return encoder_instance(obj) return encoder_instance(obj)
if include is not None and not isinstance(include, (set, dict)): if include is not None and not isinstance(include, (set, dict)):
include = set(include) include = set(include) # type: ignore[assignment]
if exclude is not None and not isinstance(exclude, (set, dict)): if exclude is not None and not isinstance(exclude, (set, dict)):
exclude = set(exclude) exclude = set(exclude) # type: ignore[assignment]
if isinstance(obj, BaseModel): if isinstance(obj, BaseModel):
obj_dict = obj.model_dump( obj_dict = obj.model_dump(
mode="json", mode="json",

View File

@ -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

View File

@ -1,4 +1,5 @@
import importlib import importlib
import sys
from types import ModuleType from types import ModuleType
from typing import Annotated, Any from typing import Annotated, Any
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
@ -12,8 +13,13 @@ from fastapi.testclient import TestClient
name="module", name="module",
params=[ params=[
"tutorial008_py39", "tutorial008_py39",
# Fails with `NameError: name 'DepA' is not defined` pytest.param(
pytest.param("tutorial008_an_py39", marks=pytest.mark.xfail), "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): def get_module(request: pytest.FixtureRequest):

View File

@ -6,8 +6,8 @@ needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires pyth
needs_py310 = pytest.mark.skipif( needs_py310 = pytest.mark.skipif(
sys.version_info < (3, 10), reason="requires python3.10+" sys.version_info < (3, 10), reason="requires python3.10+"
) )
needs_py_lt_314 = pytest.mark.skipif( needs_py314 = pytest.mark.skipif(
sys.version_info >= (3, 14), reason="requires python3.13-" sys.version_info < (3, 14), reason="requires python3.14+"
) )