Skip to content

Commit a766a58

Browse files
authored
Mark ExceptionMiddleware.http_exception as async to prevent thread creation. (#2922)
1 parent 4a81176 commit a766a58

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

starlette/middleware/exceptions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
6161

6262
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
6363

64-
def http_exception(self, request: Request, exc: Exception) -> Response:
64+
async def http_exception(self, request: Request, exc: Exception) -> Response:
6565
assert isinstance(exc, HTTPException)
6666
if exc.status_code in {204, 304}:
6767
return Response(status_code=exc.status_code, headers=exc.headers)

tests/test_exceptions.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import typing
12
from collections.abc import Generator
23

34
import pytest
5+
from pytest import MonkeyPatch
46

57
from starlette.exceptions import HTTPException, WebSocketException
68
from starlette.middleware.exceptions import ExceptionMiddleware
@@ -184,3 +186,22 @@ def test_request_in_app_and_handler_is_the_same_object(client: TestClient) -> No
184186
response = client.post("/consume_body_in_endpoint_and_handler", content=b"Hello!")
185187
assert response.status_code == 422
186188
assert response.json() == {"body": "Hello!"}
189+
190+
191+
def test_http_exception_does_not_use_threadpool(client: TestClient, monkeypatch: MonkeyPatch) -> None:
192+
"""
193+
Verify that handling HTTPException does not invoke run_in_threadpool,
194+
confirming the handler correctly runs in the main async context.
195+
"""
196+
from starlette import _exception_handler
197+
198+
# Replace run_in_threadpool with a function that raises an error
199+
def mock_run_in_threadpool(*args: typing.Any, **kwargs: typing.Any) -> None:
200+
pytest.fail("run_in_threadpool should not be called for HTTP exceptions") # pragma: no cover
201+
202+
# Apply the monkeypatch only during this test
203+
monkeypatch.setattr(_exception_handler, "run_in_threadpool", mock_run_in_threadpool)
204+
205+
# This should succeed because http_exception is async and won't use run_in_threadpool
206+
response = client.get("/not_acceptable")
207+
assert response.status_code == 406

0 commit comments

Comments
 (0)