From 85044cfd6f29a729c6a5e8d2d735c4199d64c37d Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 20:24:05 +0100 Subject: [PATCH 1/6] Add support for PEP695 TypeAliasType --- fastapi/dependencies/utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index b73473484..2c51f2a74 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -64,6 +64,12 @@ from starlette.responses import Response from starlette.websockets import WebSocket from typing_extensions import Annotated, get_args, get_origin +try: + from typing import TypeAliasType +except ImportError: + TypeAliasType = None + + multipart_not_installed_error = ( 'Form data requires "python-multipart" to be installed. \n' 'You can install "python-multipart" with: \n\n' @@ -325,6 +331,9 @@ def analyze_param( depends = None type_annotation: Any = Any use_annotation: Any = Any + if TypeAliasType is not None and isinstance(annotation, TypeAliasType): + # unpack in case py3.12 type syntax is used + annotation = annotation.__value__ if annotation is not inspect.Signature.empty: use_annotation = annotation type_annotation = annotation From 1d6f28a9020a3e40768b22a45613c5da832e1804 Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 21:12:20 +0100 Subject: [PATCH 2/6] Add tests for pepe695 compatibility --- tests/test_dependency_pep695.py | 28 ++++++++++++++++++++++++++++ tests/utils.py | 3 +++ 2 files changed, 31 insertions(+) create mode 100644 tests/test_dependency_pep695.py diff --git a/tests/test_dependency_pep695.py b/tests/test_dependency_pep695.py new file mode 100644 index 000000000..675c6a7d1 --- /dev/null +++ b/tests/test_dependency_pep695.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import Annotated + +from fastapi import Depends, FastAPI +from fastapi.testclient import TestClient +from .utils import needs_py312 + + +async def some_value() -> int: + return 123 + + +type DependedValue = Annotated[int, Depends(some_value)] + + +@needs_py312 +def test_pep695_type_dependencies(): + app = FastAPI() + + @app.get("/") + async def get_with_dep(value: DependedValue) -> str: + return f"value: {value}" + + client = TestClient(app) + response = client.get("/") + assert response.status_code == 200 + assert response.text == '"value: 123"' diff --git a/tests/utils.py b/tests/utils.py index 460c028f7..72b39f6d9 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,5 +7,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_py312 = pytest.mark.skipif( + sys.version_info < (3, 12), reason="requires python3.12+" +) needs_pydanticv2 = pytest.mark.skipif(not PYDANTIC_V2, reason="requires Pydantic v2") needs_pydanticv1 = pytest.mark.skipif(PYDANTIC_V2, reason="requires Pydantic v1") From 9d215added2511dbf377a8d5480bdfa1c0b61bff Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 21:38:44 +0100 Subject: [PATCH 3/6] Make pytest ignore py312 files for older py --- tests/conftest.py | 4 ++++ ...t_dependency_pep695.py => test_dependency_pep695_py312.py} | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/conftest.py rename tests/{test_dependency_pep695.py => test_dependency_pep695_py312.py} (89%) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..521e9f0f7 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,4 @@ +import sys + +if sys.version_info < (3, 12): + collect_ignore_glob = ["*_py312.py"] diff --git a/tests/test_dependency_pep695.py b/tests/test_dependency_pep695_py312.py similarity index 89% rename from tests/test_dependency_pep695.py rename to tests/test_dependency_pep695_py312.py index 675c6a7d1..378c9d3ee 100644 --- a/tests/test_dependency_pep695.py +++ b/tests/test_dependency_pep695_py312.py @@ -4,6 +4,7 @@ from typing import Annotated from fastapi import Depends, FastAPI from fastapi.testclient import TestClient + from .utils import needs_py312 @@ -19,7 +20,7 @@ def test_pep695_type_dependencies(): app = FastAPI() @app.get("/") - async def get_with_dep(value: DependedValue) -> str: + async def get_with_dep(value: DependedValue) -> str: # noqa return f"value: {value}" client = TestClient(app) From 4ca7f7e25fb0a767dbbf18738e4adb2cfdf7595a Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 21:45:26 +0100 Subject: [PATCH 4/6] 'fix' mypy issue --- fastapi/dependencies/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 2c51f2a74..393541405 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -65,9 +65,9 @@ from starlette.websockets import WebSocket from typing_extensions import Annotated, get_args, get_origin try: - from typing import TypeAliasType + from typing_extensions import TypeAliasType except ImportError: - TypeAliasType = None + TypeAliasType = None # type: ignore[misc,assignment] multipart_not_installed_error = ( From fb15e0dbd41a12ed7db4dfd179b14ddc203224ac Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 21:52:59 +0100 Subject: [PATCH 5/6] ignore py312 file for coverage: SyntaxError --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c23e82ef4..8a3ccf644 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,6 +137,7 @@ context = '${CONTEXT}' omit = [ "docs_src/response_model/tutorial003_04.py", "docs_src/response_model/tutorial003_04_py310.py", + "tests/test_dependency_pep695_py312.py" # syntax error for version < py312 ] [tool.ruff] From 2fa1a2e6419c36208782a5f5b173c986cd5716b9 Mon Sep 17 00:00:00 2001 From: lokidev Date: Tue, 13 Feb 2024 22:01:49 +0100 Subject: [PATCH 6/6] Exclude faulty branch from coverage --- fastapi/dependencies/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 393541405..4e3c82684 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -66,7 +66,7 @@ from typing_extensions import Annotated, get_args, get_origin try: from typing_extensions import TypeAliasType -except ImportError: +except ImportError: # pragma: no cover TypeAliasType = None # type: ignore[misc,assignment]