Don't warn on time.sleep injected by the debugger (#65420)
parent
c8e64358b1
commit
5a34feb7de
|
@ -88,7 +88,7 @@ def run_callback_threadsafe(
|
||||||
return future
|
return future
|
||||||
|
|
||||||
|
|
||||||
def check_loop(strict: bool = True) -> None:
|
def check_loop(func: Callable, strict: bool = True) -> None:
|
||||||
"""Warn if called inside the event loop. Raise if `strict` is True."""
|
"""Warn if called inside the event loop. Raise if `strict` is True."""
|
||||||
try:
|
try:
|
||||||
get_running_loop()
|
get_running_loop()
|
||||||
|
@ -101,7 +101,18 @@ def check_loop(strict: bool = True) -> None:
|
||||||
|
|
||||||
found_frame = None
|
found_frame = None
|
||||||
|
|
||||||
for frame in reversed(extract_stack()):
|
stack = extract_stack()
|
||||||
|
|
||||||
|
if (
|
||||||
|
func.__name__ == "sleep"
|
||||||
|
and len(stack) >= 3
|
||||||
|
and stack[-3].filename.endswith("pydevd.py")
|
||||||
|
):
|
||||||
|
# Don't report `time.sleep` injected by the debugger (pydevd.py)
|
||||||
|
# stack[-1] is us, stack[-2] is protected_loop_func, stack[-3] is the offender
|
||||||
|
return
|
||||||
|
|
||||||
|
for frame in reversed(stack):
|
||||||
for path in ("custom_components/", "homeassistant/components/"):
|
for path in ("custom_components/", "homeassistant/components/"):
|
||||||
try:
|
try:
|
||||||
index = frame.filename.index(path)
|
index = frame.filename.index(path)
|
||||||
|
@ -152,7 +163,7 @@ def protect_loop(func: Callable, strict: bool = True) -> Callable:
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def protected_loop_func(*args, **kwargs): # type: ignore
|
def protected_loop_func(*args, **kwargs): # type: ignore
|
||||||
check_loop(strict=strict)
|
check_loop(func, strict=strict)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
return protected_loop_func
|
return protected_loop_func
|
||||||
|
|
|
@ -5,6 +5,7 @@ from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import block_async_io
|
||||||
from homeassistant.util import async_ as hasync
|
from homeassistant.util import async_ as hasync
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,10 +71,14 @@ def test_run_callback_threadsafe_from_inside_event_loop(mock_ident, _):
|
||||||
assert len(loop.call_soon_threadsafe.mock_calls) == 2
|
assert len(loop.call_soon_threadsafe.mock_calls) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def banned_function():
|
||||||
|
"""Mock banned function."""
|
||||||
|
|
||||||
|
|
||||||
async def test_check_loop_async():
|
async def test_check_loop_async():
|
||||||
"""Test check_loop detects when called from event loop without integration context."""
|
"""Test check_loop detects when called from event loop without integration context."""
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
hasync.check_loop()
|
hasync.check_loop(banned_function)
|
||||||
|
|
||||||
|
|
||||||
async def test_check_loop_async_integration(caplog):
|
async def test_check_loop_async_integration(caplog):
|
||||||
|
@ -98,7 +103,7 @@ async def test_check_loop_async_integration(caplog):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
):
|
):
|
||||||
hasync.check_loop()
|
hasync.check_loop(banned_function)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call inside the event loop. This is causing stability issues. "
|
"Detected blocking call inside the event loop. This is causing stability issues. "
|
||||||
"Please report issue for hue doing blocking calls at "
|
"Please report issue for hue doing blocking calls at "
|
||||||
|
@ -129,7 +134,7 @@ async def test_check_loop_async_integration_non_strict(caplog):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
):
|
):
|
||||||
hasync.check_loop(strict=False)
|
hasync.check_loop(banned_function, strict=False)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call inside the event loop. This is causing stability issues. "
|
"Detected blocking call inside the event loop. This is causing stability issues. "
|
||||||
"Please report issue for hue doing blocking calls at "
|
"Please report issue for hue doing blocking calls at "
|
||||||
|
@ -160,7 +165,7 @@ async def test_check_loop_async_custom(caplog):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
):
|
):
|
||||||
hasync.check_loop()
|
hasync.check_loop(banned_function)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call inside the event loop. This is causing stability issues. "
|
"Detected blocking call inside the event loop. This is causing stability issues. "
|
||||||
"Please report issue to the custom component author for hue doing blocking calls "
|
"Please report issue to the custom component author for hue doing blocking calls "
|
||||||
|
@ -170,7 +175,7 @@ async def test_check_loop_async_custom(caplog):
|
||||||
|
|
||||||
def test_check_loop_sync(caplog):
|
def test_check_loop_sync(caplog):
|
||||||
"""Test check_loop does nothing when called from thread."""
|
"""Test check_loop does nothing when called from thread."""
|
||||||
hasync.check_loop()
|
hasync.check_loop(banned_function)
|
||||||
assert "Detected blocking call inside the event loop" not in caplog.text
|
assert "Detected blocking call inside the event loop" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,10 +184,38 @@ def test_protect_loop_sync():
|
||||||
func = Mock()
|
func = Mock()
|
||||||
with patch("homeassistant.util.async_.check_loop") as mock_check_loop:
|
with patch("homeassistant.util.async_.check_loop") as mock_check_loop:
|
||||||
hasync.protect_loop(func)(1, test=2)
|
hasync.protect_loop(func)(1, test=2)
|
||||||
mock_check_loop.assert_called_once_with(strict=True)
|
mock_check_loop.assert_called_once_with(func, strict=True)
|
||||||
func.assert_called_once_with(1, test=2)
|
func.assert_called_once_with(1, test=2)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_protect_loop_debugger_sleep(caplog):
|
||||||
|
"""Test time.sleep injected by the debugger is not reported."""
|
||||||
|
block_async_io.enable()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.util.async_.extract_stack",
|
||||||
|
return_value=[
|
||||||
|
Mock(
|
||||||
|
filename="/home/paulus/homeassistant/.venv/blah/pydevd.py",
|
||||||
|
lineno="23",
|
||||||
|
line="do_something()",
|
||||||
|
),
|
||||||
|
Mock(
|
||||||
|
filename="/home/paulus/homeassistant/util/async.py",
|
||||||
|
lineno="123",
|
||||||
|
line="protected_loop_func",
|
||||||
|
),
|
||||||
|
Mock(
|
||||||
|
filename="/home/paulus/homeassistant/util/async.py",
|
||||||
|
lineno="123",
|
||||||
|
line="check_loop()",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
):
|
||||||
|
time.sleep(0)
|
||||||
|
assert "Detected blocking call inside the event loop" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
async def test_gather_with_concurrency():
|
async def test_gather_with_concurrency():
|
||||||
"""Test gather_with_concurrency limits the number of running tasks."""
|
"""Test gather_with_concurrency limits the number of running tasks."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue