diff --git a/.gitignore b/.gitignore
index bff8fa258..25fbf0b1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ venv*/
.python-version
build/
dist/
+.idea/
diff --git a/requirements.txt b/requirements.txt
index 88d6fb011..b8aaaaaa5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,7 @@ types-PyYAML==6.0.12.20241230
types-dataclasses==0.6.6
pytest==8.3.4
trio==0.28.0
+blockbuster==1.5.13
# Documentation
black==24.10.0
diff --git a/starlette/datastructures.py b/starlette/datastructures.py
index 90a7296a0..6acf30940 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 fb2ad6e35..804743775 100644
--- a/starlette/testclient.py
+++ b/starlette/testclient.py
@@ -288,7 +288,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 7232cfd18..6c94c7578 100644
--- a/tests/middleware/test_base.py
+++ b/tests/middleware/test_base.py
@@ -468,7 +468,7 @@ async def cancel_on_disconnect(
# A timeout is set for 0.1 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 6b2080c17..d8eae97fc 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)))