This commit is contained in:
uydev 2026-02-04 17:36:50 +00:00 committed by GitHub
commit 69d1b74a08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 3 deletions

View File

@ -46,7 +46,7 @@ from fastapi.concurrency import (
contextmanager_in_threadpool,
)
from fastapi.dependencies.models import Dependant
from fastapi.exceptions import DependencyScopeError
from fastapi.exceptions import DependencyScopeError, FastAPIError
from fastapi.logger import logger
from fastapi.security.oauth2 import SecurityScopes
from fastapi.types import DependencyCacheKey
@ -121,6 +121,7 @@ def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> De
call=depends.dependency,
scope=depends.scope,
own_oauth_scopes=own_oauth_scopes,
enforce_annotation=False,
)
@ -253,6 +254,13 @@ def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
return get_typed_annotation(annotation, globalns)
def _dependency_defines_type(depends: params.Depends) -> bool:
dependency = depends.dependency
if inspect.isclass(dependency):
return True
return get_typed_return_annotation(dependency) is not None
def get_dependant(
*,
path: str,
@ -262,6 +270,7 @@ def get_dependant(
parent_oauth_scopes: Optional[list[str]] = None,
use_cache: bool = True,
scope: Union[Literal["function", "request"], None] = None,
enforce_annotation: bool = True,
) -> Dependant:
dependant = Dependant(
call=call,
@ -286,6 +295,15 @@ def get_dependant(
)
if param_details.depends is not None:
assert param_details.depends.dependency
if (
enforce_annotation
and param.annotation is inspect.Signature.empty
and not _dependency_defines_type(param_details.depends)
):
raise FastAPIError(
f'Dependency parameter "{param_name}" must have a type annotation. '
f"For example: `{param_name}: SomeType = Depends(...)`"
)
if (
(dependant.is_gen_callable or dependant.is_async_gen_callable)
and dependant.computed_scope == "request"
@ -308,6 +326,7 @@ def get_dependant(
parent_oauth_scopes=current_scopes,
use_cache=param_details.depends.use_cache,
scope=param_details.depends.scope,
enforce_annotation=False,
)
dependant.dependencies.append(sub_dependant)
continue
@ -320,7 +339,11 @@ def get_dependant(
f"Cannot specify multiple FastAPI annotations for {param_name!r}"
)
continue
assert param_details.field is not None
if enforce_annotation and param_details.field is None:
raise FastAPIError(
f'Dependency parameter "{param_name}" must have a type annotation. '
f"For example: `{param_name}: SomeType = Depends(...)`"
)
if isinstance(param_details.field.field_info, params.Body):
dependant.body_params.append(param_details.field)
else:
@ -614,6 +637,7 @@ async def solve_dependencies(
name=sub_dependant.name,
parent_oauth_scopes=sub_dependant.oauth_scopes,
scope=sub_dependant.scope,
enforce_annotation=False,
)
solved_result = await solve_dependencies(

View File

@ -456,7 +456,10 @@ class APIWebSocketRoute(routing.WebSocketRoute):
self.dependencies = list(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, scope="function"
path=self.path_format,
call=self.endpoint,
scope="function",
enforce_annotation=False,
)
for depends in self.dependencies[::-1]:
self.dependant.dependencies.insert(

View File

@ -2,6 +2,7 @@ from typing import Optional
import pytest
from fastapi import APIRouter, Depends, FastAPI
from fastapi.exceptions import FastAPIError
from fastapi.testclient import TestClient
app = FastAPI()
@ -389,3 +390,23 @@ def test_override_with_sub_router_decorator_depends_k_bar():
assert response.status_code == 200
assert response.json() == {"in": "router-decorator-depends"}
app.dependency_overrides = {}
def test_missing_type_annotation_dependency():
local_app = FastAPI()
def get_db():
return "db"
with pytest.raises(FastAPIError) as exc:
@local_app.get("/bad")
def bad(dep=Depends(get_db)):
return dep
TestClient(local_app)
msg = str(exc.value)
assert "Dependency parameter" in msg
assert "type annotation" in msg
assert "Depends" in msg