Add additional blocking operations to loop protection (#124017)

pull/124063/head
J. Nick Koston 2024-08-16 09:03:24 -05:00 committed by GitHub
parent 14a3217d7e
commit cb8a6af12d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 143 additions and 0 deletions

View File

@ -8,6 +8,7 @@ import glob
from http.client import HTTPConnection
import importlib
import os
from pathlib import Path
from ssl import SSLContext
import sys
import threading
@ -162,6 +163,60 @@ _BLOCKING_CALLS: tuple[BlockingCall, ...] = (
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=SSLContext.load_cert_chain,
object=SSLContext,
function="load_cert_chain",
check_allowed=None,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.open,
object=Path,
function="open",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.read_text,
object=Path,
function="read_text",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.read_bytes,
object=Path,
function="read_bytes",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.write_text,
object=Path,
function="write_text",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
BlockingCall(
original_func=Path.write_bytes,
object=Path,
function="write_bytes",
check_allowed=_check_file_allowed,
strict=False,
strict_core=False,
skip_for_tests=True,
),
)

View File

@ -218,6 +218,17 @@ async def test_protect_loop_open(caplog: pytest.LogCaptureFixture) -> None:
assert "Detected blocking call to open with args" not in caplog.text
async def test_protect_loop_path_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in /proc is not reported."""
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/proc/does_not_exist").open(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to open with args" not in caplog.text
async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
@ -231,6 +242,71 @@ async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None:
assert "Detected blocking call to open with args" in caplog.text
async def test_protect_path_open(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").open(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to open with args" in caplog.text
async def test_protect_path_read_bytes(caplog: pytest.LogCaptureFixture) -> None:
"""Test reading file bytes in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").read_bytes(), # noqa: ASYNC230
):
pass
assert "Detected blocking call to read_bytes with args" in caplog.text
async def test_protect_path_read_text(caplog: pytest.LogCaptureFixture) -> None:
"""Test reading a file text in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data_not_exist").read_text(encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to read_text with args" in caplog.text
async def test_protect_path_write_bytes(caplog: pytest.LogCaptureFixture) -> None:
"""Test writing file bytes in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data/not/exist").write_bytes(b"xxx"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to write_bytes with args" in caplog.text
async def test_protect_path_write_text(caplog: pytest.LogCaptureFixture) -> None:
"""Test writing file text in the event loop logs."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
with (
contextlib.suppress(FileNotFoundError),
Path("/config/data/not/exist").write_text("xxx", encoding="utf8"), # noqa: ASYNC230
):
pass
assert "Detected blocking call to write_text with args" in caplog.text
async def test_enable_multiple_times(caplog: pytest.LogCaptureFixture) -> None:
"""Test trying to enable multiple times."""
with patch.object(block_async_io, "_IN_TESTS", False):
@ -354,6 +430,18 @@ async def test_protect_loop_load_verify_locations(
assert "Detected blocking call to load_verify_locations" in caplog.text
async def test_protect_loop_load_cert_chain(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test SSLContext.load_cert_chain calls in the loop are logged."""
with patch.object(block_async_io, "_IN_TESTS", False):
block_async_io.enable()
context = ssl.create_default_context()
with pytest.raises(OSError):
context.load_cert_chain("/dev/null")
assert "Detected blocking call to load_cert_chain" in caplog.text
async def test_open_calls_ignored_in_tests(caplog: pytest.LogCaptureFixture) -> None:
"""Test opening a file in tests is ignored."""
assert block_async_io._IN_TESTS