Add additional blocking operations to loop protection (#124017)
This commit is contained in:
parent
14a3217d7e
commit
cb8a6af12d
2 changed files with 143 additions and 0 deletions
|
@ -8,6 +8,7 @@ import glob
|
||||||
from http.client import HTTPConnection
|
from http.client import HTTPConnection
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
from ssl import SSLContext
|
from ssl import SSLContext
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
@ -162,6 +163,60 @@ _BLOCKING_CALLS: tuple[BlockingCall, ...] = (
|
||||||
strict_core=False,
|
strict_core=False,
|
||||||
skip_for_tests=True,
|
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,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
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:
|
async def test_protect_open(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test opening a file in the event loop logs."""
|
"""Test opening a file in the event loop logs."""
|
||||||
with patch.object(block_async_io, "_IN_TESTS", False):
|
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
|
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:
|
async def test_enable_multiple_times(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test trying to enable multiple times."""
|
"""Test trying to enable multiple times."""
|
||||||
with patch.object(block_async_io, "_IN_TESTS", False):
|
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
|
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:
|
async def test_open_calls_ignored_in_tests(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test opening a file in tests is ignored."""
|
"""Test opening a file in tests is ignored."""
|
||||||
assert block_async_io._IN_TESTS
|
assert block_async_io._IN_TESTS
|
||||||
|
|
Loading…
Add table
Reference in a new issue