Browse Source

fix: allow Response type hint as dependency annotation (#10127)

Previously, using Response as a type hint with Depends would fail:
    def endpoint(response: Annotated[Response, Depends(modify_response)]):

This was due to an overly strict assertion that prevented Depends
from being used with Response type annotations.

The fix moves the Depends check before the special type handling,
so when a Depends is specified, the dependency is called normally
and its return value used, rather than applying the special
Response injection logic.

This allows patterns like:
- response: Annotated[Response, Depends(modify_response)]
- response: Response = Depends(modify_response)

While still supporting regular Response injection:
- response: Response

Fixes #10127
pull/14794/head
Jonathan Fulton 6 months ago
parent
commit
6b782a8f9f
  1. 5
      fastapi/dependencies/utils.py
  2. 89
      tests/test_response_dependency.py

5
fastapi/dependencies/utils.py

@ -444,7 +444,9 @@ def analyze_param(
depends = dataclasses.replace(depends, dependency=type_annotation) depends = dataclasses.replace(depends, dependency=type_annotation)
# Handle non-param type annotations like Request # Handle non-param type annotations like Request
if lenient_issubclass( # Only apply special handling when there's no explicit Depends - if there's a Depends,
# the dependency will be called and its return value used instead of the special injection
if depends is None and lenient_issubclass(
type_annotation, type_annotation,
( (
Request, Request,
@ -455,7 +457,6 @@ def analyze_param(
SecurityScopes, SecurityScopes,
), ),
): ):
assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
assert field_info is None, ( assert field_info is None, (
f"Cannot specify FastAPI annotation for type {type_annotation!r}" f"Cannot specify FastAPI annotation for type {type_annotation!r}"
) )

89
tests/test_response_dependency.py

@ -0,0 +1,89 @@
"""Test using Response type hint as dependency annotation."""
from typing import Annotated
from fastapi import Depends, FastAPI, Response
from fastapi.testclient import TestClient
def test_response_with_depends_annotated():
"""Response type hint should work with Annotated[Response, Depends(...)]."""
app = FastAPI()
def modify_response(response: Response) -> Response:
response.headers["X-Custom"] = "modified"
return response
@app.get("/")
def endpoint(response: Annotated[Response, Depends(modify_response)]):
return {"status": "ok"}
client = TestClient(app)
resp = client.get("/")
assert resp.status_code == 200
assert resp.json() == {"status": "ok"}
assert resp.headers.get("X-Custom") == "modified"
def test_response_with_depends_default():
"""Response type hint should work with Response = Depends(...)."""
app = FastAPI()
def modify_response(response: Response) -> Response:
response.headers["X-Custom"] = "modified"
return response
@app.get("/")
def endpoint(response: Response = Depends(modify_response)):
return {"status": "ok"}
client = TestClient(app)
resp = client.get("/")
assert resp.status_code == 200
assert resp.json() == {"status": "ok"}
assert resp.headers.get("X-Custom") == "modified"
def test_response_without_depends():
"""Regular Response injection should still work."""
app = FastAPI()
@app.get("/")
def endpoint(response: Response):
response.headers["X-Direct"] = "set"
return {"status": "ok"}
client = TestClient(app)
resp = client.get("/")
assert resp.status_code == 200
assert resp.json() == {"status": "ok"}
assert resp.headers.get("X-Direct") == "set"
def test_response_dependency_chain():
"""Response dependency should work in a chain of dependencies."""
app = FastAPI()
def first_modifier(response: Response) -> Response:
response.headers["X-First"] = "1"
return response
def second_modifier(
response: Annotated[Response, Depends(first_modifier)],
) -> Response:
response.headers["X-Second"] = "2"
return response
@app.get("/")
def endpoint(response: Annotated[Response, Depends(second_modifier)]):
return {"status": "ok"}
client = TestClient(app)
resp = client.get("/")
assert resp.status_code == 200
assert resp.headers.get("X-First") == "1"
assert resp.headers.get("X-Second") == "2"
Loading…
Cancel
Save