1 """asyncio loop utilities."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from functools
import cache
12 from typing
import Any
16 MissingIntegrationFrame,
18 get_integration_frame,
22 _LOGGER = logging.getLogger(__name__)
26 """Get line from cache or read from file."""
27 return (linecache.getline(filename, lineno)
or "?").strip()
32 _PREVIOUSLY_REPORTED: set[tuple[str |
None, str, int | Any]] = set()
36 func: Callable[..., Any],
37 check_allowed: Callable[[dict[str, Any]], bool] |
None =
None,
39 strict_core: bool =
True,
42 """Warn if called inside the event loop. Raise if `strict` is True."""
43 if check_allowed
is not None and check_allowed(mapped_args):
48 offender_filename = offender_frame.f_code.co_filename
49 offender_lineno = offender_frame.f_lineno
51 report_key: tuple[str |
None, str, int | Any]
55 except MissingIntegrationFrame:
57 report_key = (
None, offender_filename, offender_lineno)
58 was_reported = report_key
in _PREVIOUSLY_REPORTED
59 _PREVIOUSLY_REPORTED.add(report_key)
63 "Detected blocking call to %s with args %s in %s, "
64 "line %s: %s inside the event loop; "
65 "This is causing stability issues. "
66 "Please create a bug report at "
67 "https://github.com/home-assistant/core/issues?q=is%%3Aopen+is%%3Aissue\n"
70 mapped_args.get(
"args"),
78 "Detected blocking call to %s with args %s in %s, "
79 "line %s: %s inside the event loop; "
80 "This is causing stability issues. "
81 "Please create a bug report at "
82 "https://github.com/home-assistant/core/issues?q=is%%3Aopen+is%%3Aissue\n"
84 "Traceback (most recent call last):\n%s",
86 mapped_args.get(
"args"),
91 "".join(traceback.format_stack(f=offender_frame)),
95 if found_frame
is None:
97 f
"Caught blocking call to {func.__name__} with args {mapped_args.get("args
")} "
98 f
"in {offender_filename}, line {offender_lineno}: {offender_line} "
99 "inside the event loop; "
100 "This is causing stability issues. "
101 "Please create a bug report at "
102 "https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue\n"
103 f
"{_dev_help_message(func.__name__)}"
106 report_key = (integration_frame.integration, offender_filename, offender_lineno)
107 was_reported = report_key
in _PREVIOUSLY_REPORTED
108 _PREVIOUSLY_REPORTED.add(report_key)
112 integration_domain=integration_frame.integration,
113 module=integration_frame.module,
118 "Detected blocking call to %s with args %s "
119 "inside the event loop by %sintegration '%s' "
120 "at %s, line %s: %s (offender: %s, line %s: %s), please %s\n"
123 mapped_args.get(
"args"),
124 "custom " if integration_frame.custom_integration
else "",
125 integration_frame.integration,
126 integration_frame.relative_filename,
127 integration_frame.line_number,
128 integration_frame.line,
137 "Detected blocking call to %s with args %s "
138 "inside the event loop by %sintegration '%s' "
139 "at %s, line %s: %s (offender: %s, line %s: %s), please %s\n"
141 "Traceback (most recent call last):\n%s",
143 mapped_args.get(
"args"),
144 "custom " if integration_frame.custom_integration
else "",
145 integration_frame.integration,
146 integration_frame.relative_filename,
147 integration_frame.line_number,
148 integration_frame.line,
154 "".join(traceback.format_stack(f=integration_frame.frame)),
159 f
"Caught blocking call to {func.__name__} with args "
160 f
"{mapped_args.get('args')} inside the event loop by "
161 f
"{'custom ' if integration_frame.custom_integration else ''}"
162 f
"integration '{integration_frame.integration}' at "
163 f
"{integration_frame.relative_filename}, line {integration_frame.line_number}:"
164 f
" {integration_frame.line}. (offender: {offender_filename}, line "
165 f
"{offender_lineno}: {offender_line}), please {report_issue}\n"
166 f
"{_dev_help_message(func.__name__)}"
172 """Generate help message to guide developers."""
174 "For developers, please see "
175 "https://developers.home-assistant.io/docs/asyncio_blocking_operations/"
176 f
"#{what.replace('.', '')}"
180 def protect_loop[**_P, _R](
181 func: Callable[_P, _R],
184 strict_core: bool =
True,
185 check_allowed: Callable[[dict[str, Any]], bool] |
None =
None,
186 ) -> Callable[_P, _R]:
187 """Protect function from running in event loop."""
189 @functools.wraps(func)
190 def protected_loop_func(*args: _P.args, **kwargs: _P.kwargs) -> _R:
191 if threading.get_ident() == loop_thread_id:
195 strict_core=strict_core,
196 check_allowed=check_allowed,
200 return func(*args, **kwargs)
202 return protected_loop_func
HomeAssistant|None async_get_hass_or_none()
FrameType get_current_frame(int depth=0)
IntegrationFrame get_integration_frame(set|None exclude_integrations=None)
str async_suggest_report_issue(HomeAssistant|None hass, *Integration|None integration=None, str|None integration_domain=None, str|None module=None)
str _get_line_from_cache(str filename, int lineno)
None raise_for_blocking_call(Callable[..., Any] func, Callable[[dict[str, Any]], bool]|None check_allowed=None, bool strict=True, bool strict_core=True, **Any mapped_args)
str _dev_help_message(str what)