From c51e644203a0c93a791bc0d342d14a56728078fc Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 6 Mar 2025 13:16:50 +0100 Subject: [PATCH] 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 --- homeassistant/helpers/frame.py | 60 ++++++++++++------- .../alarm_control_panel/test_init.py | 22 +++---- tests/components/vacuum/test_init.py | 3 + tests/helpers/test_frame.py | 8 +-- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/homeassistant/helpers/frame.py b/homeassistant/helpers/frame.py index acdadb95788..ca7b097d90d 100644 --- a/homeassistant/helpers/frame.py +++ b/homeassistant/helpers/frame.py @@ -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): diff --git a/tests/components/alarm_control_panel/test_init.py b/tests/components/alarm_control_panel/test_init.py index 168d7ecc269..747a9d1a358 100644 --- a/tests/components/alarm_control_panel/test_init.py +++ b/tests/components/alarm_control_panel/test_init.py @@ -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 (.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" " (.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 diff --git a/tests/components/vacuum/test_init.py b/tests/components/vacuum/test_init.py index 5735d557288..717a69470b3 100644 --- a/tests/components/vacuum/test_init.py +++ b/tests/components/vacuum/test_init.py @@ -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) diff --git a/tests/helpers/test_frame.py b/tests/helpers/test_frame.py index 6127761d69b..6a509ffae5c 100644 --- a/tests/helpers/test_frame.py +++ b/tests/helpers/test_frame.py @@ -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", ),