This commit is contained in:
Albin Skott 2025-12-04 07:33:42 +00:00 committed by GitHub
commit 6086cb3ece
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 88 additions and 1 deletions

View File

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

View File

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