This commit is contained in:
eason 2026-03-16 10:21:04 +00:00 committed by GitHub
commit 55aaa94a84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 106 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import functools
import inspect
import json
import types
import warnings
from collections.abc import (
AsyncIterator,
Awaitable,
@ -679,6 +680,21 @@ def get_request_handler(
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = solved_result.background_tasks
elif solved_result.background_tasks is not None:
warnings.warn(
"FastAPI has injected BackgroundTasks via "
"dependency injection, but the endpoint returned "
"a Response that already has a `background` set. "
"The dependency-injected background tasks will be "
"discarded in favor of the response's own "
"`background`. To avoid this warning, either add "
"your tasks to the injected `BackgroundTasks` "
"instance instead of setting `background` on the "
"response, or do not inject `BackgroundTasks` at "
"all.",
UserWarning,
stacklevel=1,
)
response = raw_response
else:
response_args = _build_response_args(

View File

@ -0,0 +1,90 @@
"""Test that a warning is emitted when a returned Response already has a background
task set and the dependency-injected BackgroundTasks would be silently discarded.
Ref: https://github.com/fastapi/fastapi/issues/11215
"""
import warnings
from fastapi import BackgroundTasks, FastAPI
from fastapi.testclient import TestClient
from starlette.background import BackgroundTask
from starlette.responses import JSONResponse, Response
def test_warn_when_response_background_overwrites_injected_tasks():
"""When the endpoint returns a Response with its own `background`,
and the user also injected `BackgroundTasks`, a UserWarning should
be emitted so the silent data loss is visible."""
app = FastAPI()
@app.get("/")
async def endpoint(tasks: BackgroundTasks):
tasks.add_task(lambda: None)
return Response(
content="Custom response",
background=BackgroundTask(lambda: None),
)
client = TestClient(app, raise_server_exceptions=False)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
resp = client.get("/")
assert resp.status_code == 200
bg_warnings = [w for w in caught if "background" in str(w.message).lower()]
assert len(bg_warnings) == 1
assert issubclass(bg_warnings[0].category, UserWarning)
def test_no_warn_when_response_has_no_background():
"""When the endpoint returns a Response without a `background`,
no warning should be emitted (the injected tasks are attached)."""
app = FastAPI()
executed = []
def bg_task():
executed.append(True)
@app.get("/")
async def endpoint(tasks: BackgroundTasks):
tasks.add_task(bg_task)
return JSONResponse(content={"ok": True})
client = TestClient(app)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
resp = client.get("/")
assert resp.status_code == 200
bg_warnings = [w for w in caught if "background" in str(w.message).lower()]
assert len(bg_warnings) == 0
# The injected task should have run
assert executed == [True]
def test_no_warn_when_no_injected_background_tasks():
"""When the endpoint returns a Response with a `background` but did
NOT inject BackgroundTasks, no warning should be emitted."""
app = FastAPI()
executed = []
@app.get("/")
async def endpoint():
return Response(
content="ok",
background=BackgroundTask(lambda: executed.append(True)),
)
client = TestClient(app)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
resp = client.get("/")
assert resp.status_code == 200
bg_warnings = [w for w in caught if "background" in str(w.message).lower()]
assert len(bg_warnings) == 0
assert executed == [True]