Use loader.async_suggest_report_issue in async util (#101516)
This commit is contained in:
parent
5d0c8947a1
commit
4e98d39106
2 changed files with 54 additions and 50 deletions
|
@ -5,12 +5,15 @@ from asyncio import Future, Semaphore, gather, get_running_loop
|
||||||
from asyncio.events import AbstractEventLoop
|
from asyncio.events import AbstractEventLoop
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
|
from contextlib import suppress
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from traceback import extract_stack
|
from traceback import extract_stack
|
||||||
from typing import Any, ParamSpec, TypeVar
|
from typing import Any, ParamSpec, TypeVar
|
||||||
|
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_SHUTDOWN_RUN_CALLBACK_THREADSAFE = "_shutdown_run_callback_threadsafe"
|
_SHUTDOWN_RUN_CALLBACK_THREADSAFE = "_shutdown_run_callback_threadsafe"
|
||||||
|
@ -82,6 +85,14 @@ def check_loop(
|
||||||
The default advisory message is 'Use `await hass.async_add_executor_job()'
|
The default advisory message is 'Use `await hass.async_add_executor_job()'
|
||||||
Set `advise_msg` to an alternate message if the solution differs.
|
Set `advise_msg` to an alternate message if the solution differs.
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=import-outside-toplevel
|
||||||
|
from homeassistant.core import HomeAssistant, async_get_hass
|
||||||
|
from homeassistant.helpers.frame import (
|
||||||
|
MissingIntegrationFrame,
|
||||||
|
get_integration_frame,
|
||||||
|
)
|
||||||
|
from homeassistant.loader import async_suggest_report_issue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
get_running_loop()
|
get_running_loop()
|
||||||
in_loop = True
|
in_loop = True
|
||||||
|
@ -104,54 +115,47 @@ def check_loop(
|
||||||
# stack[-1] is us, stack[-2] is protected_loop_func, stack[-3] is the offender
|
# stack[-1] is us, stack[-2] is protected_loop_func, stack[-3] is the offender
|
||||||
return
|
return
|
||||||
|
|
||||||
for frame in reversed(stack):
|
try:
|
||||||
for path in ("custom_components/", "homeassistant/components/"):
|
integration_frame = get_integration_frame()
|
||||||
try:
|
except MissingIntegrationFrame:
|
||||||
index = frame.filename.index(path)
|
# Did not source from integration? Hard error.
|
||||||
found_frame = frame
|
if found_frame is None:
|
||||||
break
|
raise RuntimeError( # noqa: TRY200
|
||||||
except ValueError:
|
f"Detected blocking call to {func.__name__} inside the event loop. "
|
||||||
continue
|
f"{advise_msg or 'Use `await hass.async_add_executor_job()`'}; "
|
||||||
|
"This is causing stability issues. Please create a bug report at "
|
||||||
|
f"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
||||||
|
)
|
||||||
|
|
||||||
if found_frame is not None:
|
hass: HomeAssistant | None = None
|
||||||
break
|
with suppress(HomeAssistantError):
|
||||||
|
hass = async_get_hass()
|
||||||
# Did not source from integration? Hard error.
|
report_issue = async_suggest_report_issue(
|
||||||
if found_frame is None:
|
hass,
|
||||||
raise RuntimeError(
|
integration_domain=integration_frame.integration,
|
||||||
f"Detected blocking call to {func.__name__} inside the event loop. "
|
module=integration_frame.module,
|
||||||
f"{advise_msg or 'Use `await hass.async_add_executor_job()`'}; "
|
)
|
||||||
"This is causing stability issues. Please report issue"
|
|
||||||
)
|
|
||||||
|
|
||||||
start = index + len(path)
|
|
||||||
end = found_frame.filename.index("/", start)
|
|
||||||
|
|
||||||
integration = found_frame.filename[start:end]
|
|
||||||
|
|
||||||
if path == "custom_components/":
|
|
||||||
extra = " to the custom integration author"
|
|
||||||
else:
|
|
||||||
extra = ""
|
|
||||||
|
|
||||||
|
found_frame = integration_frame.frame
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
(
|
(
|
||||||
"Detected blocking call to %s inside the event loop. This is causing"
|
"Detected blocking call to %s inside the event loop by %sintegration '%s' "
|
||||||
" stability issues. Please report issue%s for %s doing blocking calls at"
|
"at %s, line %s: %s, please %s"
|
||||||
" %s, line %s: %s"
|
|
||||||
),
|
),
|
||||||
func.__name__,
|
func.__name__,
|
||||||
extra,
|
"custom " if integration_frame.custom_integration else "",
|
||||||
integration,
|
integration_frame.integration,
|
||||||
found_frame.filename[index:],
|
integration_frame.relative_filename,
|
||||||
found_frame.lineno,
|
found_frame.lineno,
|
||||||
(found_frame.line or "?").strip(),
|
(found_frame.line or "?").strip(),
|
||||||
|
report_issue,
|
||||||
)
|
)
|
||||||
|
|
||||||
if strict:
|
if strict:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Blocking calls must be done in the executor or a separate thread;"
|
"Blocking calls must be done in the executor or a separate thread;"
|
||||||
f" {advise_msg or 'Use `await hass.async_add_executor_job()`'}; at"
|
f" {advise_msg or 'Use `await hass.async_add_executor_job()`'}; at"
|
||||||
f" {found_frame.filename[index:]}, line {found_frame.lineno}:"
|
f" {integration_frame.relative_filename}, line {found_frame.lineno}:"
|
||||||
f" {(found_frame.line or '?').strip()}"
|
f" {(found_frame.line or '?').strip()}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ async def test_check_loop_async() -> None:
|
||||||
async def test_check_loop_async_integration(caplog: pytest.LogCaptureFixture) -> None:
|
async def test_check_loop_async_integration(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test check_loop detects and raises when called from event loop from integration context."""
|
"""Test check_loop detects and raises when called from event loop from integration context."""
|
||||||
with pytest.raises(RuntimeError), patch(
|
with pytest.raises(RuntimeError), patch(
|
||||||
"homeassistant.util.async_.extract_stack",
|
"homeassistant.helpers.frame.extract_stack",
|
||||||
return_value=[
|
return_value=[
|
||||||
Mock(
|
Mock(
|
||||||
filename="/home/paulus/homeassistant/core.py",
|
filename="/home/paulus/homeassistant/core.py",
|
||||||
|
@ -69,10 +69,10 @@ async def test_check_loop_async_integration(caplog: pytest.LogCaptureFixture) ->
|
||||||
):
|
):
|
||||||
hasync.check_loop(banned_function)
|
hasync.check_loop(banned_function)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call to banned_function inside the event loop. This is "
|
"Detected blocking call to banned_function inside the event loop by integration"
|
||||||
"causing stability issues. Please report issue for hue doing blocking calls at "
|
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on, "
|
||||||
"homeassistant/components/hue/light.py, line 23: self.light.is_on"
|
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
||||||
in caplog.text
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22" in caplog.text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ async def test_check_loop_async_integration_non_strict(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test check_loop detects when called from event loop from integration context."""
|
"""Test check_loop detects when called from event loop from integration context."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.util.async_.extract_stack",
|
"homeassistant.helpers.frame.extract_stack",
|
||||||
return_value=[
|
return_value=[
|
||||||
Mock(
|
Mock(
|
||||||
filename="/home/paulus/homeassistant/core.py",
|
filename="/home/paulus/homeassistant/core.py",
|
||||||
|
@ -102,17 +102,17 @@ async def test_check_loop_async_integration_non_strict(
|
||||||
):
|
):
|
||||||
hasync.check_loop(banned_function, strict=False)
|
hasync.check_loop(banned_function, strict=False)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call to banned_function inside the event loop. This is "
|
"Detected blocking call to banned_function inside the event loop by integration"
|
||||||
"causing stability issues. Please report issue for hue doing blocking calls at "
|
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on, "
|
||||||
"homeassistant/components/hue/light.py, line 23: self.light.is_on"
|
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
||||||
in caplog.text
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22" in caplog.text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_check_loop_async_custom(caplog: pytest.LogCaptureFixture) -> None:
|
async def test_check_loop_async_custom(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
"""Test check_loop detects when called from event loop with custom component context."""
|
"""Test check_loop detects when called from event loop with custom component context."""
|
||||||
with pytest.raises(RuntimeError), patch(
|
with pytest.raises(RuntimeError), patch(
|
||||||
"homeassistant.util.async_.extract_stack",
|
"homeassistant.helpers.frame.extract_stack",
|
||||||
return_value=[
|
return_value=[
|
||||||
Mock(
|
Mock(
|
||||||
filename="/home/paulus/homeassistant/core.py",
|
filename="/home/paulus/homeassistant/core.py",
|
||||||
|
@ -133,10 +133,10 @@ async def test_check_loop_async_custom(caplog: pytest.LogCaptureFixture) -> None
|
||||||
):
|
):
|
||||||
hasync.check_loop(banned_function)
|
hasync.check_loop(banned_function)
|
||||||
assert (
|
assert (
|
||||||
"Detected blocking call to banned_function inside the event loop. This is"
|
"Detected blocking call to banned_function inside the event loop by custom "
|
||||||
" causing stability issues. Please report issue to the custom integration"
|
"integration 'hue' at custom_components/hue/light.py, line 23: self.light.is_on"
|
||||||
" author for hue doing blocking calls at custom_components/hue/light.py, line"
|
", please create a bug report at https://github.com/home-assistant/core/issues?"
|
||||||
" 23: self.light.is_on"
|
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22"
|
||||||
) in caplog.text
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue