Adjust blocking I/O messages to provide developer help (#120113)
parent
2ad5b1c3a6
commit
f7e194b32c
|
@ -31,14 +31,9 @@ def raise_for_blocking_call(
|
|||
check_allowed: Callable[[dict[str, Any]], bool] | None = None,
|
||||
strict: bool = True,
|
||||
strict_core: bool = True,
|
||||
advise_msg: str | None = None,
|
||||
**mapped_args: Any,
|
||||
) -> None:
|
||||
"""Warn if called inside the event loop. Raise if `strict` is True.
|
||||
|
||||
The default advisory message is 'Use `await hass.async_add_executor_job()'
|
||||
Set `advise_msg` to an alternate message if the solution differs.
|
||||
"""
|
||||
"""Warn if called inside the event loop. Raise if `strict` is True."""
|
||||
if check_allowed is not None and check_allowed(mapped_args):
|
||||
return
|
||||
|
||||
|
@ -55,24 +50,31 @@ def raise_for_blocking_call(
|
|||
if not strict_core:
|
||||
_LOGGER.warning(
|
||||
"Detected blocking call to %s with args %s in %s, "
|
||||
"line %s: %s inside the event loop\n"
|
||||
"line %s: %s inside the event loop; "
|
||||
"This is causing stability issues. "
|
||||
"Please create a bug report at "
|
||||
"https://github.com/home-assistant/core/issues?q=is%%3Aopen+is%%3Aissue\n"
|
||||
"%s\n"
|
||||
"Traceback (most recent call last):\n%s",
|
||||
func.__name__,
|
||||
mapped_args.get("args"),
|
||||
offender_filename,
|
||||
offender_lineno,
|
||||
offender_line,
|
||||
_dev_help_message(func.__name__),
|
||||
"".join(traceback.format_stack(f=offender_frame)),
|
||||
)
|
||||
return
|
||||
|
||||
if found_frame is None:
|
||||
raise RuntimeError( # noqa: TRY200
|
||||
f"Detected blocking call to {func.__name__} inside the event loop "
|
||||
f"in {offender_filename}, line {offender_lineno}: {offender_line}. "
|
||||
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"
|
||||
f"Caught blocking call to {func.__name__} with args {mapped_args.get("args")} "
|
||||
f"in {offender_filename}, line {offender_lineno}: {offender_line} "
|
||||
"inside the event loop; "
|
||||
"This is causing stability issues. "
|
||||
"Please create a bug report at "
|
||||
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue\n"
|
||||
f"{_dev_help_message(func.__name__)}"
|
||||
)
|
||||
|
||||
report_issue = async_suggest_report_issue(
|
||||
|
@ -82,10 +84,13 @@ def raise_for_blocking_call(
|
|||
)
|
||||
|
||||
_LOGGER.warning(
|
||||
"Detected blocking call to %s inside the event loop by %sintegration '%s' "
|
||||
"Detected blocking call to %s with args %s "
|
||||
"inside the event loop by %sintegration '%s' "
|
||||
"at %s, line %s: %s (offender: %s, line %s: %s), please %s\n"
|
||||
"%s\n"
|
||||
"Traceback (most recent call last):\n%s",
|
||||
func.__name__,
|
||||
mapped_args.get("args"),
|
||||
"custom " if integration_frame.custom_integration else "",
|
||||
integration_frame.integration,
|
||||
integration_frame.relative_filename,
|
||||
|
@ -95,19 +100,32 @@ def raise_for_blocking_call(
|
|||
offender_lineno,
|
||||
offender_line,
|
||||
report_issue,
|
||||
_dev_help_message(func.__name__),
|
||||
"".join(traceback.format_stack(f=integration_frame.frame)),
|
||||
)
|
||||
|
||||
if strict:
|
||||
raise RuntimeError(
|
||||
"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" {integration_frame.relative_filename}, line {integration_frame.line_number}:"
|
||||
f" {integration_frame.line} "
|
||||
f"(offender: {offender_filename}, line {offender_lineno}: {offender_line})"
|
||||
"Caught blocking call to {func.__name__} with args "
|
||||
f"{mapped_args.get('args')} inside the event loop by"
|
||||
f"{'custom ' if integration_frame.custom_integration else ''}"
|
||||
"integration '{integration_frame.integration}' at "
|
||||
f"{integration_frame.relative_filename}, line {integration_frame.line_number}:"
|
||||
f" {integration_frame.line}. (offender: {offender_filename}, line "
|
||||
f"{offender_lineno}: {offender_line}), please {report_issue}\n"
|
||||
f"{_dev_help_message(func.__name__)}"
|
||||
)
|
||||
|
||||
|
||||
def _dev_help_message(what: str) -> str:
|
||||
"""Generate help message to guide developers."""
|
||||
return (
|
||||
"For developers, please see "
|
||||
"https://developers.home-assistant.io/docs/asyncio_blocking_operations/"
|
||||
f"#{what.replace('.', '')}"
|
||||
)
|
||||
|
||||
|
||||
def protect_loop[**_P, _R](
|
||||
func: Callable[_P, _R],
|
||||
loop_thread_id: int,
|
||||
|
|
|
@ -61,9 +61,7 @@ async def test_protect_loop_sleep() -> None:
|
|||
]
|
||||
)
|
||||
with (
|
||||
pytest.raises(
|
||||
RuntimeError, match="Detected blocking call to sleep inside the event loop"
|
||||
),
|
||||
pytest.raises(RuntimeError, match="Caught blocking call to sleep with args"),
|
||||
patch(
|
||||
"homeassistant.block_async_io.get_current_frame",
|
||||
return_value=frames,
|
||||
|
@ -89,9 +87,7 @@ async def test_protect_loop_sleep_get_current_frame_raises() -> None:
|
|||
]
|
||||
)
|
||||
with (
|
||||
pytest.raises(
|
||||
RuntimeError, match="Detected blocking call to sleep inside the event loop"
|
||||
),
|
||||
pytest.raises(RuntimeError, match="Caught blocking call to sleep with args"),
|
||||
patch(
|
||||
"homeassistant.block_async_io.get_current_frame",
|
||||
side_effect=ValueError,
|
||||
|
@ -204,7 +200,8 @@ async def test_protect_loop_importlib_import_module_in_integration(
|
|||
importlib.import_module("not_loaded_module")
|
||||
|
||||
assert (
|
||||
"Detected blocking call to import_module inside the event loop by "
|
||||
"Detected blocking call to import_module with args ('not_loaded_module',) "
|
||||
"inside the event loop by "
|
||||
"integration 'hue' at homeassistant/components/hue/light.py, line 23"
|
||||
) in caplog.text
|
||||
|
||||
|
|
|
@ -28,6 +28,14 @@ async def test_raise_for_blocking_call_async_non_strict_core(
|
|||
haloop.raise_for_blocking_call(banned_function, strict_core=False)
|
||||
assert "Detected blocking call to banned_function" in caplog.text
|
||||
assert "Traceback (most recent call last)" in caplog.text
|
||||
assert (
|
||||
"Please create a bug report at https://github.com/home-assistant/core/issues"
|
||||
in caplog.text
|
||||
)
|
||||
assert (
|
||||
"For developers, please see "
|
||||
"https://developers.home-assistant.io/docs/asyncio_blocking_operations/#banned_function"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
async def test_raise_for_blocking_call_async_integration(
|
||||
|
@ -74,12 +82,17 @@ async def test_raise_for_blocking_call_async_integration(
|
|||
):
|
||||
haloop.raise_for_blocking_call(banned_function)
|
||||
assert (
|
||||
"Detected blocking call to banned_function inside the event loop by integration"
|
||||
"Detected blocking call to banned_function with args None"
|
||||
" inside the event loop by integration"
|
||||
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on "
|
||||
"(offender: /home/paulus/aiohue/lights.py, line 2: mock_line), please create "
|
||||
"a bug report at https://github.com/home-assistant/core/issues?"
|
||||
"q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+hue%22" in caplog.text
|
||||
)
|
||||
assert (
|
||||
"For developers, please see "
|
||||
"https://developers.home-assistant.io/docs/asyncio_blocking_operations/#banned_function"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
async def test_raise_for_blocking_call_async_integration_non_strict(
|
||||
|
@ -125,7 +138,8 @@ async def test_raise_for_blocking_call_async_integration_non_strict(
|
|||
):
|
||||
haloop.raise_for_blocking_call(banned_function, strict=False)
|
||||
assert (
|
||||
"Detected blocking call to banned_function inside the event loop by integration"
|
||||
"Detected blocking call to banned_function with args None"
|
||||
" inside the event loop by integration"
|
||||
" 'hue' at homeassistant/components/hue/light.py, line 23: self.light.is_on "
|
||||
"(offender: /home/paulus/aiohue/lights.py, line 2: mock_line), "
|
||||
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
||||
|
@ -136,6 +150,14 @@ async def test_raise_for_blocking_call_async_integration_non_strict(
|
|||
'File "/home/paulus/homeassistant/components/hue/light.py", line 23'
|
||||
in caplog.text
|
||||
)
|
||||
assert (
|
||||
"please create a bug report at https://github.com/home-assistant/core/issues"
|
||||
in caplog.text
|
||||
)
|
||||
assert (
|
||||
"For developers, please see "
|
||||
"https://developers.home-assistant.io/docs/asyncio_blocking_operations/#banned_function"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
async def test_raise_for_blocking_call_async_custom(
|
||||
|
@ -182,7 +204,8 @@ async def test_raise_for_blocking_call_async_custom(
|
|||
):
|
||||
haloop.raise_for_blocking_call(banned_function)
|
||||
assert (
|
||||
"Detected blocking call to banned_function inside the event loop by custom "
|
||||
"Detected blocking call to banned_function with args None"
|
||||
" inside the event loop by custom "
|
||||
"integration 'hue' at custom_components/hue/light.py, line 23: self.light.is_on"
|
||||
" (offender: /home/paulus/aiohue/lights.py, line 2: mock_line), "
|
||||
"please create a bug report at https://github.com/home-assistant/core/issues?"
|
||||
|
@ -193,6 +216,10 @@ async def test_raise_for_blocking_call_async_custom(
|
|||
'File "/home/paulus/config/custom_components/hue/light.py", line 23'
|
||||
in caplog.text
|
||||
)
|
||||
assert (
|
||||
"For developers, please see "
|
||||
"https://developers.home-assistant.io/docs/asyncio_blocking_operations/#banned_function"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
async def test_raise_for_blocking_call_sync(
|
||||
|
|
Loading…
Reference in New Issue