Browse Source

Vendor a copy of is_typealiastype from typing-inspection

pull/13920/head
Albin Skott 1 week ago
parent
commit
50b9bd5d8b
Failed to extract signature
  1. 35
      fastapi/dependencies/utils.py
  2. 1
      pyproject.toml
  3. 4
      tests/conftest.py
  4. 10
      tests/test_dependency_pep695.py
  5. 3
      tests/utils.py

35
fastapi/dependencies/utils.py

@ -1,4 +1,6 @@
import inspect import inspect
import sys
import typing
from contextlib import AsyncExitStack, contextmanager from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy from copy import copy, deepcopy
from dataclasses import dataclass from dataclasses import dataclass
@ -19,6 +21,7 @@ from typing import (
) )
import anyio import anyio
import typing_extensions
from fastapi import params from fastapi import params
from fastapi._compat import ( from fastapi._compat import (
PYDANTIC_V2, PYDANTIC_V2,
@ -71,13 +74,12 @@ from starlette.datastructures import (
from starlette.requests import HTTPConnection, Request from starlette.requests import HTTPConnection, Request
from starlette.responses import Response from starlette.responses import Response
from starlette.websockets import WebSocket from starlette.websockets import WebSocket
from typing_extensions import Annotated, get_args, get_origin from typing_extensions import Annotated, TypeAliasType, TypeGuard, get_args, get_origin
try: try:
from typing_extensions import TypeAliasType from types import GenericAlias
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
TypeAliasType = None # type: ignore[misc,assignment] GenericAlias = None # type: ignore[misc,assignment]
multipart_not_installed_error = ( multipart_not_installed_error = (
'Form data requires "python-multipart" to be installed. \n' 'Form data requires "python-multipart" to be installed. \n'
@ -362,7 +364,7 @@ def analyze_param(
depends = None depends = None
type_annotation: Any = Any type_annotation: Any = Any
use_annotation: Any = Any use_annotation: Any = Any
if TypeAliasType is not None and isinstance(annotation, TypeAliasType): if _is_typealiastype(annotation):
# unpack in case py3.12 type syntax is used # unpack in case py3.12 type syntax is used
annotation = annotation.__value__ annotation = annotation.__value__
if annotation is not inspect.Signature.empty: if annotation is not inspect.Signature.empty:
@ -1008,3 +1010,26 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs), field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
) )
return final_field return final_field
def _is_typealiastype(tp: Any, /) -> TypeGuard[TypeAliasType]:
in_typing = hasattr(typing, "TypeAliasType")
in_typing_extensions = hasattr(typing_extensions, "TypeAliasType")
is_typealiastype = False
if in_typing and in_typing_extensions:
if getattr(typing, "TypeAliasType", None) is getattr(
typing_extensions, "TypeAliasType", None
): # pragma: no cover
is_typealiastype = isinstance(tp, typing.TypeAliasType) # type: ignore [attr-defined]
else:
is_typealiastype = isinstance(
tp,
(typing.TypeAliasType, typing_extensions.TypeAliasType), # type: ignore [attr-defined]
)
elif in_typing and not in_typing_extensions: # pragma: no cover
is_typealiastype = isinstance(tp, typing.TypeAliasType) # type: ignore [attr-defined]
elif not in_typing and in_typing_extensions:
is_typealiastype = isinstance(tp, typing_extensions.TypeAliasType)
if sys.version_info[:2] == (3, 10):
return type(tp) is not GenericAlias and is_typealiastype
return is_typealiastype

1
pyproject.toml

@ -199,7 +199,6 @@ dynamic_context = "test_function"
omit = [ omit = [
"docs_src/response_model/tutorial003_04.py", "docs_src/response_model/tutorial003_04.py",
"docs_src/response_model/tutorial003_04_py310.py", "docs_src/response_model/tutorial003_04_py310.py",
"tests/test_dependency_pep695_py312.py" # syntax error for version < py312
] ]
[tool.coverage.report] [tool.coverage.report]

4
tests/conftest.py

@ -1,4 +0,0 @@
import sys
if sys.version_info < (3, 12):
collect_ignore_glob = ["*_py312.py"]

10
tests/test_dependency_pep695_py312.py → tests/test_dependency_pep695.py

@ -1,21 +1,19 @@
from __future__ import annotations from __future__ import annotations
from typing import Annotated
from fastapi import Depends, FastAPI from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from typing_extensions import Annotated, TypeAliasType
from .utils import needs_py312
async def some_value() -> int: async def some_value() -> int:
return 123 return 123
type DependedValue = Annotated[int, Depends(some_value)] DependedValue = TypeAliasType(
"DependedValue", Annotated[int, Depends(some_value)], type_params=()
)
@needs_py312
def test_pep695_type_dependencies(): def test_pep695_type_dependencies():
app = FastAPI() app = FastAPI()

3
tests/utils.py

@ -8,9 +8,6 @@ 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_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_pydanticv2 = pytest.mark.skipif(not PYDANTIC_V2, reason="requires Pydantic v2")
needs_pydanticv1 = pytest.mark.skipif(PYDANTIC_V2, reason="requires Pydantic v1") needs_pydanticv1 = pytest.mark.skipif(PYDANTIC_V2, reason="requires Pydantic v1")

Loading…
Cancel
Save