diff --git a/requirements.txt b/requirements.txt
index 7529e605c..233e91c3f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,7 @@ types-PyYAML==6.0.12.20250326
types-dataclasses==0.6.6
pytest==8.3.5
trio==0.29.0
+blockbuster==1.5.23
# Documentation
black==25.1.0
diff --git a/starlette/datastructures.py b/starlette/datastructures.py
index f5d74d25f..8c52d7a11 100644
--- a/starlette/datastructures.py
+++ b/starlette/datastructures.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import sys
import typing
from shlex import shlex
from urllib.parse import SplitResult, parse_qsl, urlencode, urlsplit
@@ -438,7 +439,7 @@ async def write(self, data: bytes) -> None:
if self.size is not None:
self.size += len(data)
- if self._in_memory:
+ if self._in_memory and self.file.tell() + len(data) <= getattr(self.file, "_max_size", sys.maxsize):
self.file.write(data)
else:
await run_in_threadpool(self.file.write, data)
diff --git a/starlette/middleware/errors.py b/starlette/middleware/errors.py
index 76ad776be..6ca0495f8 100644
--- a/starlette/middleware/errors.py
+++ b/starlette/middleware/errors.py
@@ -6,6 +6,8 @@
import traceback
import typing
+import anyio
+
from starlette._utils import is_async_callable
from starlette.concurrency import run_in_threadpool
from starlette.requests import Request
@@ -167,7 +169,7 @@ async def _send(message: Message) -> None:
request = Request(scope)
if self.debug:
# In debug mode, return traceback responses.
- response = self.debug_response(request, exc)
+ response = await anyio.to_thread.run_sync(self.debug_response, request, exc)
elif self.handler is None:
# Use our default 500 error handler.
response = self.error_response(request, exc)
diff --git a/starlette/testclient.py b/starlette/testclient.py
index d54025e52..83b9097bf 100644
--- a/starlette/testclient.py
+++ b/starlette/testclient.py
@@ -289,7 +289,7 @@ async def receive() -> Message:
await response_complete.wait()
return {"type": "http.disconnect"}
- body = request.read()
+ body = await anyio.to_thread.run_sync(request.read)
if isinstance(body, str):
body_bytes: bytes = body.encode("utf-8") # pragma: no cover
elif body is None:
diff --git a/tests/conftest.py b/tests/conftest.py
index 4db3ae018..e2b5b90a4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,14 +1,23 @@
from __future__ import annotations
import functools
+from collections.abc import Iterator
from typing import Any, Literal
import pytest
+from blockbuster import blockbuster_ctx
from starlette.testclient import TestClient
from tests.types import TestClientFactory
+@pytest.fixture(autouse=True)
+def blockbuster() -> Iterator[None]:
+ with blockbuster_ctx("starlette") as bb:
+ bb.functions["os.stat"].can_block_in("/mimetypes.py", "init")
+ yield
+
+
@pytest.fixture
def test_client_factory(
anyio_backend_name: Literal["asyncio", "trio"],
diff --git a/tests/middleware/test_base.py b/tests/middleware/test_base.py
index e4e82077f..7eefea829 100644
--- a/tests/middleware/test_base.py
+++ b/tests/middleware/test_base.py
@@ -489,9 +489,9 @@ async def cancel_on_disconnect(
# before we start returning the body
await task_group.start(cancel_on_disconnect)
- # A timeout is set for 0.1 second in order to ensure that
+ # A timeout is set for 0.2 second in order to ensure that
# we never deadlock the test run in an infinite loop
- with anyio.move_on_after(0.1):
+ with anyio.move_on_after(0.2):
while True:
await send(
{
diff --git a/tests/test_templates.py b/tests/test_templates.py
index e182cb82b..ad87760cd 100644
--- a/tests/test_templates.py
+++ b/tests/test_templates.py
@@ -23,7 +23,7 @@ def test_templates(tmpdir: Path, test_client_factory: TestClientFactory) -> None
with open(path, "w") as file:
file.write("Hello, world")
- async def homepage(request: Request) -> Response:
+ def homepage(request: Request) -> Response:
return templates.TemplateResponse(request, "index.html")
app = Starlette(debug=True, routes=[Route("/", endpoint=homepage)])
@@ -40,7 +40,7 @@ def test_calls_context_processors(tmp_path: Path, test_client_factory: TestClien
path = tmp_path / "index.html"
path.write_text("Hello {{ username }}")
- async def homepage(request: Request) -> Response:
+ def homepage(request: Request) -> Response:
return templates.TemplateResponse(request, "index.html")
def hello_world_processor(request: Request) -> dict[str, str]:
@@ -69,7 +69,7 @@ def test_template_with_middleware(tmpdir: Path, test_client_factory: TestClientF
with open(path, "w") as file:
file.write("Hello, world")
- async def homepage(request: Request) -> Response:
+ def homepage(request: Request) -> Response:
return templates.TemplateResponse(request, "index.html")
class CustomMiddleware(BaseHTTPMiddleware):
@@ -96,7 +96,7 @@ def test_templates_with_directories(tmp_path: Path, test_client_factory: TestCli
template_a = dir_a / "template_a.html"
template_a.write_text(" a")
- async def page_a(request: Request) -> Response:
+ def page_a(request: Request) -> Response:
return templates.TemplateResponse(request, "template_a.html")
dir_b = tmp_path.resolve() / "b"
@@ -104,7 +104,7 @@ async def page_a(request: Request) -> Response:
template_b = dir_b / "template_b.html"
template_b.write_text(" b")
- async def page_b(request: Request) -> Response:
+ def page_b(request: Request) -> Response:
return templates.TemplateResponse(request, "template_b.html")
app = Starlette(
@@ -150,7 +150,7 @@ def test_templates_with_environment(tmpdir: Path, test_client_factory: TestClien
with open(path, "w") as file:
file.write("Hello, world")
- async def homepage(request: Request) -> Response:
+ def homepage(request: Request) -> Response:
return templates.TemplateResponse(request, "index.html")
env = jinja2.Environment(loader=jinja2.FileSystemLoader(str(tmpdir)))