Support Annotated dependencies in APIRoute

This commit is contained in:
ProfiAnton 2025-10-16 02:57:36 +02:00
parent c9b8f6ff2d
commit 0253ac7273
3 changed files with 57 additions and 38 deletions

View File

@ -333,7 +333,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends | Any]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of global dependencies, they will be applied to each
@ -1141,7 +1141,7 @@ class FastAPI(Starlette):
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
dependencies: Optional[Sequence[Depends | routing.AnnotatedType]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
@ -1199,7 +1199,7 @@ class FastAPI(Starlette):
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
dependencies: Optional[Sequence[Depends | routing.AnnotatedType]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
@ -1258,7 +1258,7 @@ class FastAPI(Starlette):
endpoint: Callable[..., Any],
name: Optional[str] = None,
*,
dependencies: Optional[Sequence[Depends]] = None,
dependencies: Optional[Sequence[Depends | routing.AnnotatedType]] = None,
) -> None:
self.router.add_api_websocket_route(
path,
@ -1287,7 +1287,7 @@ class FastAPI(Starlette):
] = None,
*,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be used for this
@ -1352,7 +1352,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to all the
@ -1611,7 +1611,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -1984,7 +1984,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -2362,7 +2362,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -2740,7 +2740,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3113,7 +3113,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3486,7 +3486,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3859,7 +3859,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -4237,7 +4237,7 @@ class FastAPI(Starlette):
),
] = None,
dependencies: Annotated[
Optional[Sequence[Depends]],
Optional[Sequence[Depends | routing.AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the

View File

@ -17,6 +17,7 @@ from typing import (
List,
Mapping,
Optional,
Protocol,
Sequence,
Set,
Tuple,
@ -464,6 +465,10 @@ def get_websocket_app(
return app
class AnnotatedType(Protocol):
"""this forces mypy to check if Any is handled correctly"""
class APIWebSocketRoute(routing.WebSocketRoute):
def __init__(
self,
@ -471,13 +476,15 @@ class APIWebSocketRoute(routing.WebSocketRoute):
endpoint: Callable[..., Any],
*,
name: Optional[str] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
dependencies: Optional[Sequence[params.Depends | AnnotatedType]] = None,
dependency_overrides_provider: Optional[Any] = None,
) -> None:
self.path = path
self.endpoint = endpoint
self.name = get_name(endpoint) if name is None else name
self.dependencies = list(dependencies or [])
self.dependencies = [
get_depends_from_annotated(dep) for dep in dependencies or []
]
self.path_regex, self.path_format, self.param_convertors = compile_path(path)
self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
for depends in self.dependencies[::-1]:
@ -513,7 +520,7 @@ class APIRoute(routing.Route):
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
dependencies: Optional[Sequence[params.Depends | AnnotatedType]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
@ -606,7 +613,9 @@ class APIRoute(routing.Route):
else:
self.response_field = None # type: ignore
self.secure_cloned_response_field = None
self.dependencies = list(dependencies or [])
self.dependencies = [
get_depends_from_annotated(dep) for dep in dependencies or []
]
self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "")
# if a "form feed" character (page break) is found in the description text,
# truncate description text to the content preceding the first "form feed"
@ -671,7 +680,7 @@ class APIRoute(routing.Route):
return match, child_scope
def get_depends_from_annotated(dep: params.Depends | Any) -> params.Depends:
def get_depends_from_annotated(dep: params.Depends | AnnotatedType) -> params.Depends:
if isinstance(dep, params.Depends):
return dep
d = analyze_param(
@ -733,7 +742,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends | Any]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies to be applied to all the *path operations* in
@ -987,7 +996,7 @@ class APIRouter(routing.Router):
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
dependencies: Optional[Sequence[params.Depends | AnnotatedType]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
@ -1024,7 +1033,9 @@ class APIRouter(routing.Router):
current_tags.extend(tags)
current_dependencies = self.dependencies.copy()
if dependencies:
current_dependencies.extend(dependencies)
current_dependencies.extend(
[get_depends_from_annotated(dep) for dep in dependencies]
)
current_callbacks = self.callbacks.copy()
if callbacks:
current_callbacks.extend(callbacks)
@ -1068,7 +1079,7 @@ class APIRouter(routing.Router):
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
dependencies: Optional[Sequence[params.Depends | AnnotatedType]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
@ -1129,11 +1140,13 @@ class APIRouter(routing.Router):
endpoint: Callable[..., Any],
name: Optional[str] = None,
*,
dependencies: Optional[Sequence[params.Depends]] = None,
dependencies: Optional[Sequence[params.Depends | AnnotatedType]] = None,
) -> None:
current_dependencies = self.dependencies.copy()
if dependencies:
current_dependencies.extend(dependencies)
current_dependencies.extend(
[get_depends_from_annotated(dep) for dep in dependencies]
)
route = APIWebSocketRoute(
self.prefix + path,
@ -1164,7 +1177,7 @@ class APIRouter(routing.Router):
] = None,
*,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be used for this
@ -1240,7 +1253,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to all the
@ -1386,7 +1399,9 @@ class APIRouter(routing.Router):
current_tags.extend(route.tags)
current_dependencies: List[params.Depends] = []
if dependencies:
current_dependencies.extend(dependencies)
current_dependencies.extend(
[get_depends_from_annotated(dep) for dep in dependencies]
)
if route.dependencies:
current_dependencies.extend(route.dependencies)
current_callbacks = []
@ -1442,7 +1457,9 @@ class APIRouter(routing.Router):
elif isinstance(route, APIWebSocketRoute):
current_dependencies = []
if dependencies:
current_dependencies.extend(dependencies)
current_dependencies.extend(
[get_depends_from_annotated(dep) for dep in dependencies]
)
if route.dependencies:
current_dependencies.extend(route.dependencies)
self.add_api_websocket_route(
@ -1538,7 +1555,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -1915,7 +1932,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -2297,7 +2314,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -2679,7 +2696,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3056,7 +3073,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3433,7 +3450,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -3815,7 +3832,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
@ -4197,7 +4214,7 @@ class APIRouter(routing.Router):
),
] = None,
dependencies: Annotated[
Optional[Sequence[params.Depends]],
Optional[Sequence[params.Depends | AnnotatedType]],
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the

View File

@ -10,7 +10,7 @@ def get_value() -> int:
ValueDep = Annotated[int, Depends(get_value)]
router = APIRouter(dependencies=[ValueDep, Depends(lambda: "sdfgh")])
router = APIRouter(dependencies=[Depends(lambda: "sdfgh"), ValueDep])
@router.get("/")
@ -26,6 +26,8 @@ def no_dep():
app = FastAPI()
app.include_router(router)
router.post("/", dependencies=[Depends(lambda: None)])
def test_apirouter_annotated_dependencies():
client = TestClient(app)