mirror of https://github.com/tiangolo/fastapi.git
Merge eb544d5095 into 811fa89875
This commit is contained in:
commit
6086cb3ece
|
|
@ -1,5 +1,7 @@
|
||||||
import dataclasses
|
import dataclasses
|
||||||
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
|
||||||
|
|
@ -20,6 +22,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,
|
||||||
|
|
@ -75,7 +78,19 @@ 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, Literal, get_args, get_origin
|
from typing_extensions import (
|
||||||
|
Annotated,
|
||||||
|
Literal,
|
||||||
|
TypeAliasType,
|
||||||
|
TypeGuard,
|
||||||
|
get_args,
|
||||||
|
get_origin,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from types import GenericAlias
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
GenericAlias = None # type: ignore[misc,assignment]
|
||||||
|
|
||||||
from .. import temp_pydantic_v1_params
|
from .. import temp_pydantic_v1_params
|
||||||
|
|
||||||
|
|
@ -352,6 +367,9 @@ def analyze_param(
|
||||||
depends = None
|
depends = None
|
||||||
type_annotation: Any = Any
|
type_annotation: Any = Any
|
||||||
use_annotation: Any = Any
|
use_annotation: Any = Any
|
||||||
|
if _is_typealiastype(annotation):
|
||||||
|
# unpack in case PEP 695 type syntax is used
|
||||||
|
annotation = annotation.__value__
|
||||||
if annotation is not inspect.Signature.empty:
|
if annotation is not inspect.Signature.empty:
|
||||||
use_annotation = annotation
|
use_annotation = annotation
|
||||||
type_annotation = annotation
|
type_annotation = annotation
|
||||||
|
|
@ -1035,3 +1053,45 @@ 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]:
|
||||||
|
"""
|
||||||
|
This implementation is in line with the implementation of `is_typealiastype` in `typing-inspection`:
|
||||||
|
See:
|
||||||
|
- https://github.com/pydantic/typing-inspection/blob/8db011350942f33ac4b5d7db60d4d9ea83ab480f/src/typing_inspection/typing_objects.py#L488-L499
|
||||||
|
- https://github.com/pydantic/typing-inspection/blob/8db011350942f33ac4b5d7db60d4d9ea83ab480f/src/typing_inspection/typing_objects.py#L105-L134
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check if TypeAliasType exists in typing and/or typing_extensions.
|
||||||
|
in_typing = hasattr(typing, "TypeAliasType")
|
||||||
|
in_typing_extensions = hasattr(typing_extensions, "TypeAliasType")
|
||||||
|
|
||||||
|
is_typealiastype = False # Default: assume not a TypeAliasType
|
||||||
|
if in_typing and in_typing_extensions:
|
||||||
|
if getattr(typing, "TypeAliasType", None) is getattr(
|
||||||
|
typing_extensions, "TypeAliasType", None
|
||||||
|
): # pragma: no cover
|
||||||
|
# Case 1: Both implementations are the same object.
|
||||||
|
# Checking against one of them.
|
||||||
|
is_typealiastype = isinstance(tp, typing.TypeAliasType) # type: ignore [attr-defined]
|
||||||
|
else:
|
||||||
|
# Case 2: Implementations are different objects.
|
||||||
|
# Need to check against both versions.
|
||||||
|
is_typealiastype = isinstance(
|
||||||
|
tp,
|
||||||
|
(typing.TypeAliasType, typing_extensions.TypeAliasType), # type: ignore [attr-defined]
|
||||||
|
)
|
||||||
|
elif in_typing and not in_typing_extensions: # pragma: no cover
|
||||||
|
# Case 3: Only typing.TypeAliasType exists.
|
||||||
|
is_typealiastype = isinstance(tp, typing.TypeAliasType) # type: ignore [attr-defined]
|
||||||
|
elif not in_typing and in_typing_extensions:
|
||||||
|
# Case 4: Only typing_extensions.TypeAliasType exists.
|
||||||
|
is_typealiastype = isinstance(tp, typing_extensions.TypeAliasType)
|
||||||
|
|
||||||
|
# Special case for Python 3.10:
|
||||||
|
# On Python 3.10, parameterized PEP 695 type aliases are represented as `GenericAlias`
|
||||||
|
# instead of proper TypeAliasType. We must exclude those.
|
||||||
|
if sys.version_info[:2] == (3, 10):
|
||||||
|
return type(tp) is not GenericAlias and is_typealiastype
|
||||||
|
return is_typealiastype
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import Depends, FastAPI
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from typing_extensions import Annotated, TypeAliasType
|
||||||
|
|
||||||
|
|
||||||
|
async def some_value() -> int:
|
||||||
|
return 123
|
||||||
|
|
||||||
|
|
||||||
|
DependedValue = TypeAliasType(
|
||||||
|
"DependedValue", Annotated[int, Depends(some_value)], type_params=()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pep695_type_dependencies():
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def get_with_dep(value: DependedValue) -> str: # noqa
|
||||||
|
return f"value: {value}"
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.text == '"value: 123"'
|
||||||
Loading…
Reference in New Issue