Add ability to pass integration domain to report_usage (#130705)

* Add ability to pass integration domain to report_usage

* Adjust

* Fix

* Add tests

* Update test_frame.py

* Update test_frame.py

* Update test_frame.py

* Update test_frame.py

* Update test_frame.py

* Update test_frame.py

* Finish tests

* Docstring

* Replace logger warning with report_usage

* Improve

* docstring

* Improve tests

* Adjust docstring for exclude_integrations

* Fix behavior and improve tests
This commit is contained in:
epenet 2024-11-21 16:01:36 +01:00 committed by GitHub
parent 42dcfae6e7
commit 9add3a6c9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 178 additions and 22 deletions

View File

@ -2887,18 +2887,12 @@ class ConfigFlow(ConfigEntryBaseFlow):
) -> ConfigFlowResult:
"""Finish config flow and create a config entry."""
if self.source in {SOURCE_REAUTH, SOURCE_RECONFIGURE}:
report_issue = async_suggest_report_issue(
self.hass, integration_domain=self.handler
)
_LOGGER.warning(
(
"Detected %s config flow creating a new entry, "
"when it is expected to update an existing entry and abort. "
"This will stop working in %s, please %s"
),
self.source,
"2025.11",
report_issue,
report_usage(
f"creates a new entry in a '{self.source}' flow, "
"when it is expected to update an existing entry and abort",
core_behavior=ReportBehavior.LOG,
breaks_in_ha_version="2025.11",
integration_domain=self.handler,
)
result = super().async_create_entry(
title=title,

View File

@ -15,9 +15,13 @@ from typing import Any, cast
from propcache import cached_property
from homeassistant.core import async_get_hass_or_none
from homeassistant.core import HomeAssistant, async_get_hass_or_none
from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import async_suggest_report_issue
from homeassistant.loader import (
Integration,
async_get_issue_integration,
async_suggest_report_issue,
)
_LOGGER = logging.getLogger(__name__)
@ -186,6 +190,7 @@ def report_usage(
core_integration_behavior: ReportBehavior = ReportBehavior.LOG,
custom_integration_behavior: ReportBehavior = ReportBehavior.LOG,
exclude_integrations: set[str] | None = None,
integration_domain: str | None = None,
level: int = logging.WARNING,
) -> None:
"""Report incorrect code usage.
@ -194,12 +199,29 @@ def report_usage(
Please create a bug report at https://..."
:param breaks_in_ha_version: if set, the report will be adjusted to specify the
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
"""
try:
integration_frame = get_integration_frame(
exclude_integrations=exclude_integrations
)
except MissingIntegrationFrame as err:
if integration := async_get_issue_integration(
hass := async_get_hass_or_none(), integration_domain
):
_report_integration_domain(
hass,
what,
breaks_in_ha_version,
integration,
core_integration_behavior,
custom_integration_behavior,
level,
)
return
msg = f"Detected code that {what}. Please report this issue"
if core_behavior is ReportBehavior.ERROR:
raise RuntimeError(msg) from err
@ -217,7 +239,7 @@ def report_usage(
integration_behavior = custom_integration_behavior
if integration_behavior is not ReportBehavior.IGNORE:
_report_integration(
_report_integration_frame(
what,
breaks_in_ha_version,
integration_frame,
@ -226,14 +248,64 @@ def report_usage(
)
def _report_integration(
def _report_integration_domain(
hass: HomeAssistant | None,
what: str,
breaks_in_ha_version: str | None,
integration: Integration,
core_integration_behavior: ReportBehavior,
custom_integration_behavior: ReportBehavior,
level: int,
) -> None:
"""Report incorrect usage in an integration (identified via domain).
Async friendly.
"""
integration_behavior = core_integration_behavior
if not integration.is_built_in:
integration_behavior = custom_integration_behavior
if integration_behavior is ReportBehavior.IGNORE:
return
# Keep track of integrations already reported to prevent flooding
key = f"{integration.domain}:{what}"
if (
integration_behavior is not ReportBehavior.ERROR
and key in _REPORTED_INTEGRATIONS
):
return
_REPORTED_INTEGRATIONS.add(key)
report_issue = async_suggest_report_issue(hass, integration=integration)
integration_type = "" if integration.is_built_in else "custom "
_LOGGER.log(
level,
"Detected that %sintegration '%s' %s. %s %s",
integration_type,
integration.domain,
what,
f"This will stop working in Home Assistant {breaks_in_ha_version}, please"
if breaks_in_ha_version
else "Please",
report_issue,
)
if integration_behavior is ReportBehavior.ERROR:
raise RuntimeError(
f"Detected that {integration_type}integration "
f"'{integration.domain}' {what}. Please {report_issue}"
)
def _report_integration_frame(
what: str,
breaks_in_ha_version: str | None,
integration_frame: IntegrationFrame,
level: int = logging.WARNING,
error: bool = False,
) -> None:
"""Report incorrect usage in an integration.
"""Report incorrect usage in an integration (identified via frame).
Async friendly.
"""

View File

@ -7,6 +7,7 @@ import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers import frame
from homeassistant.loader import async_get_integration
from tests.common import extract_stack_to_frame
@ -445,3 +446,89 @@ async def test_report(
assert errored == expected_error
assert caplog.text.count(what) == expected_log
@pytest.mark.parametrize(
("behavior", "integration_domain", "source", "logs_again"),
[
pytest.param(
"core_behavior",
None,
"code that",
True,
id="core",
),
pytest.param(
"core_behavior",
"unknown_integration",
"code that",
True,
id="unknown integration",
),
pytest.param(
"core_integration_behavior",
"sensor",
"that integration 'sensor'",
False,
id="core integration",
),
pytest.param(
"custom_integration_behavior",
"test_package",
"that custom integration 'test_package'",
False,
id="custom integration",
),
],
)
@pytest.mark.usefixtures("enable_custom_integrations")
async def test_report_integration_domain(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
behavior: str,
integration_domain: str | None,
source: str,
logs_again: bool,
) -> None:
"""Test report."""
await async_get_integration(hass, "sensor")
await async_get_integration(hass, "test_package")
what = "test_report_string"
lookup_text = f"Detected {source} {what}"
caplog.clear()
frame.report_usage(
what,
**{behavior: frame.ReportBehavior.IGNORE},
integration_domain=integration_domain,
)
assert lookup_text not in caplog.text
with patch.object(frame, "_REPORTED_INTEGRATIONS", set()):
frame.report_usage(
what,
**{behavior: frame.ReportBehavior.LOG},
integration_domain=integration_domain,
)
assert lookup_text in caplog.text
# Check that it does not log again
caplog.clear()
frame.report_usage(
what,
**{behavior: frame.ReportBehavior.LOG},
integration_domain=integration_domain,
)
assert (lookup_text in caplog.text) == logs_again
# Check that it raises
with pytest.raises(RuntimeError, match=lookup_text):
frame.report_usage(
what,
**{behavior: frame.ReportBehavior.ERROR},
integration_domain=integration_domain,
)

View File

@ -7157,7 +7157,10 @@ async def test_create_entry_reauth_reconfigure(
assert len(hass.config_entries.async_entries("test")) == 1
with mock_config_flow("test", TestFlow):
with (
mock_config_flow("test", TestFlow),
patch.object(frame, "_REPORTED_INTEGRATIONS", set()),
):
result = await getattr(entry, f"start_{source}_flow")(hass)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
@ -7169,10 +7172,10 @@ async def test_create_entry_reauth_reconfigure(
assert entries[0].entry_id != entry.entry_id
assert (
f"Detected {source} config flow creating a new entry, when it is expected "
"to update an existing entry and abort. This will stop working in "
"2025.11, please create a bug report at https://github.com/home"
"-assistant/core/issues?q=is%3Aopen+is%3Aissue+"
f"Detected that integration 'test' creates a new entry in a '{source}' flow, "
"when it is expected to update an existing entry and abort. This will stop "
"working in Home Assistant 2025.11, please create a bug report at "
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+"
"label%3A%22integration%3A+test%22"
) in caplog.text