Prioritize integration_domain passed to helper.frame.report_usage (#139819)

* Prioritize integration_domain passed to helper.frame.report_usage

* Update tests

* Update tests

* Improve docstring

* Rename according to suggestion
pull/139923/head
Erik Montnemery 2025-03-06 13:16:50 +01:00 committed by GitHub
parent dc4464a347
commit c51e644203
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 35 deletions

View File

@ -180,8 +180,8 @@ def report_usage(
breaking version
:param exclude_integrations: skip specified integration when reviewing the stack.
If no integration is found, the core behavior will be applied
:param integration_domain: fallback for identifying the integration if the
frame is not found
:param integration_domain: domain of the integration causing the issue. If None, the
stack frame will be searched to identify the integration causing the issue.
"""
if (hass := _hass.hass) is None:
raise RuntimeError("Frame helper not set up")
@ -220,13 +220,9 @@ def _report_usage(
Must be called from the event loop.
"""
try:
integration_frame = get_integration_frame(
exclude_integrations=exclude_integrations
)
except MissingIntegrationFrame as err:
if integration_domain:
if integration := async_get_issue_integration(hass, integration_domain):
_report_integration_domain(
_report_usage_integration_domain(
hass,
what,
breaks_in_ha_version,
@ -236,16 +232,15 @@ def _report_usage(
level,
)
return
msg = f"Detected code that {what}. Please report this issue"
if core_behavior is ReportBehavior.ERROR:
raise RuntimeError(msg) from err
if core_behavior is ReportBehavior.LOG:
if breaks_in_ha_version:
msg = (
f"Detected code that {what}. This will stop working in Home "
f"Assistant {breaks_in_ha_version}, please report this issue"
)
_LOGGER.warning(msg, stack_info=True)
_report_usage_no_integration(what, core_behavior, breaks_in_ha_version, None)
return
try:
integration_frame = get_integration_frame(
exclude_integrations=exclude_integrations
)
except MissingIntegrationFrame as err:
_report_usage_no_integration(what, core_behavior, breaks_in_ha_version, err)
return
integration_behavior = core_integration_behavior
@ -253,7 +248,7 @@ def _report_usage(
integration_behavior = custom_integration_behavior
if integration_behavior is not ReportBehavior.IGNORE:
_report_integration_frame(
_report_usage_integration_frame(
hass,
what,
breaks_in_ha_version,
@ -263,7 +258,7 @@ def _report_usage(
)
def _report_integration_domain(
def _report_usage_integration_domain(
hass: HomeAssistant | None,
what: str,
breaks_in_ha_version: str | None,
@ -313,7 +308,7 @@ def _report_integration_domain(
)
def _report_integration_frame(
def _report_usage_integration_frame(
hass: HomeAssistant,
what: str,
breaks_in_ha_version: str | None,
@ -362,6 +357,29 @@ def _report_integration_frame(
)
def _report_usage_no_integration(
what: str,
core_behavior: ReportBehavior,
breaks_in_ha_version: str | None,
err: MissingIntegrationFrame | None,
) -> None:
"""Report incorrect usage without an integration.
This could happen because the offending call happened outside of an integration,
or because the integration could not be identified.
"""
msg = f"Detected code that {what}. Please report this issue"
if core_behavior is ReportBehavior.ERROR:
raise RuntimeError(msg) from err
if core_behavior is ReportBehavior.LOG:
if breaks_in_ha_version:
msg = (
f"Detected code that {what}. This will stop working in Home "
f"Assistant {breaks_in_ha_version}, please report this issue"
)
_LOGGER.warning(msg, stack_info=True)
def warn_use[_CallableT: Callable](func: _CallableT, what: str) -> _CallableT:
"""Mock a function to warn when it was about to be used."""
if asyncio.iscoroutinefunction(func):

View File

@ -292,13 +292,13 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_state_prop
assert state is not None
assert (
"Detected that custom integration 'alarm_control_panel' is setting state"
" directly. Entity None (<class 'tests.components.alarm_control_panel."
"Detected that custom integration 'test' is setting state "
"directly. Entity None (<class 'tests.components.alarm_control_panel."
"test_init.test_alarm_control_panel_log_deprecated_state_warning_using"
"_state_prop.<locals>.MockLegacyAlarmControlPanel'>) should implement"
" the 'alarm_state' property and return its state using the AlarmControlPanelState"
" enum at test_init.py, line 123: yield. This will stop working in Home Assistant"
" 2025.11, please create a bug report at" in caplog.text
" enum. This will stop working in Home Assistant 2025.11, please report it to"
" the author of the 'test' custom integration" in caplog.text
)
@ -345,6 +345,7 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_attr_state
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
built_in=False,
)
setup_test_component_platform(
hass, ALARM_CONTROL_PANEL_DOMAIN, [entity], from_config_entry=True
@ -355,7 +356,7 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_attr_state
assert state is not None
assert (
"Detected that custom integration 'alarm_control_panel' is setting state directly."
"Detected that custom integration 'test' is setting state directly."
not in caplog.text
)
@ -364,14 +365,14 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_attr_state
)
assert (
"Detected that custom integration 'alarm_control_panel' is setting state directly."
"Detected that custom integration 'test' is setting state directly."
" Entity alarm_control_panel.test_alarm_control_panel"
" (<class 'tests.components.alarm_control_panel.test_init."
"test_alarm_control_panel_log_deprecated_state_warning_using_attr_state_attr."
"<locals>.MockLegacyAlarmControlPanel'>) should implement the 'alarm_state' property"
" and return its state using the AlarmControlPanelState enum at test_init.py, line 123:"
" yield. This will stop working in Home Assistant 2025.11,"
" please create a bug report at" in caplog.text
" and return its state using the AlarmControlPanelState enum. "
"This will stop working in Home Assistant 2025.11, please report "
"it to the author of the 'test' custom integration" in caplog.text
)
caplog.clear()
await help_test_async_alarm_control_panel_service(
@ -379,7 +380,7 @@ async def test_alarm_control_panel_log_deprecated_state_warning_using_attr_state
)
# Test we only log once
assert (
"Detected that custom integration 'alarm_control_panel' is setting state directly."
"Detected that custom integration 'test' is setting state directly."
not in caplog.text
)
@ -428,6 +429,7 @@ async def test_alarm_control_panel_deprecated_state_does_not_break_state(
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
built_in=False,
)
setup_test_component_platform(
hass, ALARM_CONTROL_PANEL_DOMAIN, [entity], from_config_entry=True

View File

@ -356,6 +356,7 @@ async def test_vacuum_log_deprecated_state_warning_using_state_prop(
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
built_in=False,
)
setup_test_component_platform(hass, VACUUM_DOMAIN, [entity], from_config_entry=True)
assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -399,6 +400,7 @@ async def test_vacuum_log_deprecated_state_warning_using_attr_state_attr(
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
built_in=False,
)
setup_test_component_platform(hass, VACUUM_DOMAIN, [entity], from_config_entry=True)
assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -463,6 +465,7 @@ async def test_vacuum_deprecated_state_does_not_break_state(
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
built_in=False,
)
setup_test_component_platform(hass, VACUUM_DOMAIN, [entity], from_config_entry=True)
assert await hass.config_entries.async_setup(config_entry.entry_id)

View File

@ -538,21 +538,21 @@ async def test_report_error_if_integration(
False,
id="custom integration",
),
# Assert integration found in stack frame has priority over integration_domain
# Assert integration_domain has priority over integration found in stack frame
pytest.param(
"core_integration_behavior",
"sensor",
"homeassistant/components/hue",
"that integration 'hue'",
"that integration 'sensor'",
False,
id="core integration stack mismatch",
),
# Assert integration found in stack frame has priority over integration_domain
# Assert integration_domain has priority over integration found in stack frame
pytest.param(
"custom_integration_behavior",
"test_package",
"custom_components/hue",
"that custom integration 'hue'",
"that custom integration 'test_package'",
False,
id="custom integration stack mismatch",
),