Browse Source

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

pull/14089/head
Motov Yurii 4 months ago
committed by GitHub
parent
commit
2fbf6f31fd
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      docs/en/docs/release-notes.md
  2. 7
      fastapi/dependencies/utils.py
  3. 4
      fastapi/encoders.py
  4. 30
      tests/test_stringified_annotation_dependency_py314.py
  5. 10
      tests/test_tutorial/test_dependencies/test_tutorial008.py
  6. 4
      tests/utils.py

2
docs/en/docs/release-notes.md

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

7
fastapi/dependencies/utils.py

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

4
fastapi/encoders.py

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

30
tests/test_stringified_annotation_dependency_py314.py

@ -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
tests/test_tutorial/test_dependencies/test_tutorial008.py

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

4
tests/utils.py

@ -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+"
) )

Loading…
Cancel
Save