-
-
Notifications
You must be signed in to change notification settings - Fork 963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect blocking calls in coroutines using BlockBuster #2858
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Httpx |
||
if isinstance(body, str): | ||
body_bytes: bytes = body.encode("utf-8") # pragma: no cover | ||
elif body is None: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
yield | ||
|
||
|
||
@pytest.fixture | ||
def test_client_factory( | ||
anyio_backend_name: Literal["asyncio", "trio"], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -466,9 +466,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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BlockBuster has a small toll on performance. |
||
while True: | ||
await send( | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ def test_templates(tmpdir: Path, test_client_factory: TestClientFactory) -> None | |
with open(path, "w") as file: | ||
file.write("<html>Hello, <a href='{{ url_for('homepage') }}'>world</a></html>") | ||
|
||
async def homepage(request: Request) -> Response: | ||
def homepage(request: Request) -> Response: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it OK to change ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It loads the test_templates.py:27: in homepage
return templates.TemplateResponse(request, "index.html")
../starlette/templating.py:208: in TemplateResponse
template = self.get_template(name)
../starlette/templating.py:131: in get_template
return self.env.get_template(name)
../venv/lib/python3.11/site-packages/jinja2/environment.py:1016: in get_template
return self._load_template(name, globals)
../venv/lib/python3.11/site-packages/jinja2/environment.py:975: in _load_template
template = self.loader.load(self, name, self.make_globals(globals))
../venv/lib/python3.11/site-packages/jinja2/loaders.py:126: in load
source, filename, uptodate = self.get_source(environment, name)
../venv/lib/python3.11/site-packages/jinja2/loaders.py:204: in get_source
if os.path.isfile(filename):
|
||
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("<html>Hello {{ username }}</html>") | ||
|
||
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("<html>Hello, <a href='{{ url_for('homepage') }}'>world</a></html>") | ||
|
||
async def homepage(request: Request) -> Response: | ||
def homepage(request: Request) -> Response: | ||
return templates.TemplateResponse(request, "index.html") | ||
|
||
class CustomMiddleware(BaseHTTPMiddleware): | ||
|
@@ -96,15 +96,15 @@ def test_templates_with_directories(tmp_path: Path, test_client_factory: TestCli | |
template_a = dir_a / "template_a.html" | ||
template_a.write_text("<html><a href='{{ url_for('page_a') }}'></a> a</html>") | ||
|
||
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" | ||
dir_b.mkdir() | ||
template_b = dir_b / "template_b.html" | ||
template_b.write_text("<html><a href='{{ url_for('page_b') }}'></a> b</html>") | ||
|
||
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("<html>Hello, <a href='{{ url_for('homepage') }}'>world</a></html>") | ||
|
||
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))) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's this about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the data to write makes the file exceed the
SpooledTemporaryFile
_max_size
,self.file.write
will do a blockingrollover
operation.