Improve error reporting when an integration tries to create a task in a thread (#115307)

This commit is contained in:
J. Nick Koston 2024-04-09 11:11:22 -10:00 committed by GitHub
parent 6ed2190c29
commit f527fd0947
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 86 additions and 6 deletions

View File

@ -24,12 +24,20 @@ def create_eager_task(
loop: AbstractEventLoop | None = None,
) -> Task[_T]:
"""Create a task from a coroutine and schedule it to run immediately."""
return Task(
coro,
loop=loop or get_running_loop(),
name=name,
eager_start=True,
)
if not loop:
try:
loop = get_running_loop()
except RuntimeError:
# If there is no running loop, create_eager_task is being called from
# the wrong thread.
# Late import to avoid circular dependencies
# pylint: disable-next=import-outside-toplevel
from homeassistant.helpers import frame
frame.report("attempted to create an asyncio task from a thread")
raise
return Task(coro, loop=loop, name=name, eager_start=True)
def cancelling(task: Future[Any]) -> bool:

View File

@ -9,6 +9,8 @@ import pytest
from homeassistant.core import HomeAssistant
from homeassistant.util import async_ as hasync
from tests.common import extract_stack_to_frame
@patch("concurrent.futures.Future")
@patch("threading.get_ident")
@ -123,3 +125,73 @@ async def test_create_eager_task_312(hass: HomeAssistant) -> None:
assert events == ["eager", "normal"]
await task1
await task2
async def test_create_eager_task_from_thread(hass: HomeAssistant) -> None:
"""Test we report trying to create an eager task from a thread."""
def create_task():
hasync.create_eager_task(asyncio.sleep(0))
with pytest.raises(
RuntimeError,
match=(
"Detected code that attempted to create an asyncio task from a thread. Please report this issue."
),
):
await hass.async_add_executor_job(create_task)
async def test_create_eager_task_from_thread_in_integration(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test we report trying to create an eager task from a thread."""
def create_task():
hasync.create_eager_task(asyncio.sleep(0))
frames = extract_stack_to_frame(
[
Mock(
filename="/home/paulus/homeassistant/core.py",
lineno="23",
line="do_something()",
),
Mock(
filename="/home/paulus/homeassistant/components/hue/light.py",
lineno="23",
line="self.light.is_on",
),
Mock(
filename="/home/paulus/aiohue/lights.py",
lineno="2",
line="something()",
),
]
)
with (
pytest.raises(RuntimeError, match="no running event loop"),
patch(
"homeassistant.helpers.frame.linecache.getline",
return_value="self.light.is_on",
),
patch(
"homeassistant.util.loop._get_line_from_cache",
return_value="mock_line",
),
patch(
"homeassistant.util.loop.get_current_frame",
return_value=frames,
),
patch(
"homeassistant.helpers.frame.get_current_frame",
return_value=frames,
),
):
await hass.async_add_executor_job(create_task)
assert (
"Detected that integration 'hue' attempted to create an asyncio task "
"from a thread at homeassistant/components/hue/light.py, line 23: "
"self.light.is_on"
) in caplog.text