mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
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:
parent
42dcfae6e7
commit
9add3a6c9b
@ -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,
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user