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 inspect
import sys
import typing
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
from dataclasses import dataclass
@ -20,6 +22,7 @@ from typing import (
)
import anyio
import typing_extensions
from fastapi import params
from fastapi._compat import (
PYDANTIC_V2,
@ -75,7 +78,19 @@ from starlette.datastructures import (
from starlette.requests import HTTPConnection, Request
from starlette.responses import Response
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
@ -352,6 +367,9 @@ def analyze_param(
depends = None
type_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:
use_annotation = annotation
type_annotation = annotation
@ -1035,3 +1053,45 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
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"'