Log directory blocking I/O functions that run in the event loop (#118529)

* Log directory I/O functions that run in the event loop

* tests
This commit is contained in:
J. Nick Koston 2024-05-31 10:09:19 -05:00 committed by GitHub
parent 1fef4fa1f6
commit 6656f7d6b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 71 additions and 0 deletions

View file

@ -2,8 +2,10 @@
import builtins
from contextlib import suppress
import glob
from http.client import HTTPConnection
import importlib
import os
import sys
import threading
import time
@ -59,8 +61,22 @@ def enable() -> None:
loop_thread_id=loop_thread_id,
)
glob.glob = protect_loop(
glob.glob, strict_core=False, strict=False, loop_thread_id=loop_thread_id
)
glob.iglob = protect_loop(
glob.iglob, strict_core=False, strict=False, loop_thread_id=loop_thread_id
)
if not _IN_TESTS:
# Prevent files being opened inside the event loop
os.listdir = protect_loop( # type: ignore[assignment]
os.listdir, strict_core=False, strict=False, loop_thread_id=loop_thread_id
)
os.scandir = protect_loop( # type: ignore[assignment]
os.scandir, strict_core=False, strict=False, loop_thread_id=loop_thread_id
)
builtins.open = protect_loop( # type: ignore[assignment]
builtins.open,
strict_core=False,

View file

@ -1,7 +1,9 @@
"""Tests for async util methods from Python source."""
import contextlib
import glob
import importlib
import os
from pathlib import Path, PurePosixPath
import time
from typing import Any
@ -10,6 +12,7 @@ from unittest.mock import Mock, patch
import pytest
from homeassistant import block_async_io
from homeassistant.core import HomeAssistant
from tests.common import extract_stack_to_frame
@ -235,3 +238,55 @@ async def test_protect_open_path(path: Any, caplog: pytest.LogCaptureFixture) ->
open(path).close()
assert "Detected blocking call to open with args" in caplog.text
async def test_protect_loop_glob(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test glob calls in the loop are logged."""
block_async_io.enable()
glob.glob("/dev/null")
assert "Detected blocking call to glob with args" in caplog.text
caplog.clear()
await hass.async_add_executor_job(glob.glob, "/dev/null")
assert "Detected blocking call to glob with args" not in caplog.text
async def test_protect_loop_iglob(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test iglob calls in the loop are logged."""
block_async_io.enable()
glob.iglob("/dev/null")
assert "Detected blocking call to iglob with args" in caplog.text
caplog.clear()
await hass.async_add_executor_job(glob.iglob, "/dev/null")
assert "Detected blocking call to iglob with args" not in caplog.text
async def test_protect_loop_scandir(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test glob calls in the loop are logged."""
block_async_io.enable()
with contextlib.suppress(FileNotFoundError):
os.scandir("/path/that/does/not/exists")
assert "Detected blocking call to scandir with args" in caplog.text
caplog.clear()
with contextlib.suppress(FileNotFoundError):
await hass.async_add_executor_job(os.scandir, "/path/that/does/not/exists")
assert "Detected blocking call to listdir with args" not in caplog.text
async def test_protect_loop_listdir(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test listdir calls in the loop are logged."""
block_async_io.enable()
with contextlib.suppress(FileNotFoundError):
os.listdir("/path/that/does/not/exists")
assert "Detected blocking call to listdir with args" in caplog.text
caplog.clear()
with contextlib.suppress(FileNotFoundError):
await hass.async_add_executor_job(os.listdir, "/path/that/does/not/exists")
assert "Detected blocking call to listdir with args" not in caplog.text