Add loader.async_suggest_report_issue and loader.async_get_issue_tracker (#101336)
* Add loader.async_suggest_report_issue and loader.async_get_issue_tracker * Update tests * Add tests * Address review comments * Address review commentspull/90928/head^2
parent
3aa6771835
commit
17779c5f0c
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||
from abc import ABC
|
||||
import asyncio
|
||||
from collections.abc import Coroutine, Iterable, Mapping, MutableMapping
|
||||
from contextlib import suppress
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from enum import Enum, auto
|
||||
|
@ -50,11 +49,7 @@ from homeassistant.exceptions import (
|
|||
InvalidStateError,
|
||||
NoEntitySpecifiedError,
|
||||
)
|
||||
from homeassistant.loader import (
|
||||
IntegrationNotLoaded,
|
||||
async_get_loaded_integration,
|
||||
bind_hass,
|
||||
)
|
||||
from homeassistant.loader import async_suggest_report_issue, bind_hass
|
||||
from homeassistant.util import ensure_unique_string, slugify
|
||||
|
||||
from . import device_registry as dr, entity_registry as er
|
||||
|
@ -1257,35 +1252,12 @@ class Entity(ABC):
|
|||
|
||||
def _suggest_report_issue(self) -> str:
|
||||
"""Suggest to report an issue."""
|
||||
report_issue = ""
|
||||
|
||||
integration = None
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
if self.platform:
|
||||
with suppress(IntegrationNotLoaded):
|
||||
integration = async_get_loaded_integration(
|
||||
self.hass, self.platform.platform_name
|
||||
)
|
||||
|
||||
if "custom_components" in type(self).__module__:
|
||||
if integration and integration.issue_tracker:
|
||||
report_issue = f"create a bug report at {integration.issue_tracker}"
|
||||
else:
|
||||
report_issue = "report it to the custom integration author"
|
||||
else:
|
||||
report_issue = (
|
||||
"create a bug report at "
|
||||
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
||||
)
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
if self.platform:
|
||||
report_issue += (
|
||||
f"+label%3A%22integration%3A+{self.platform.platform_name}%22"
|
||||
)
|
||||
|
||||
return report_issue
|
||||
platform_name = self.platform.platform_name if self.platform else None
|
||||
return async_suggest_report_issue(
|
||||
self.hass, integration_domain=platform_name, module=type(self).__module__
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
|
|
|
@ -1187,3 +1187,55 @@ def _lookup_path(hass: HomeAssistant) -> list[str]:
|
|||
def is_component_module_loaded(hass: HomeAssistant, module: str) -> bool:
|
||||
"""Test if a component module is loaded."""
|
||||
return module in hass.data[DATA_COMPONENTS]
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_issue_tracker(
|
||||
hass: HomeAssistant | None,
|
||||
*,
|
||||
integration_domain: str | None = None,
|
||||
module: str | None = None,
|
||||
) -> str | None:
|
||||
"""Return a URL for an integration's issue tracker."""
|
||||
issue_tracker = (
|
||||
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
||||
)
|
||||
if not integration_domain and not module:
|
||||
# If we know nothing about the entity, suggest opening an issue on HA core
|
||||
return issue_tracker
|
||||
|
||||
if hass and integration_domain:
|
||||
with suppress(IntegrationNotLoaded):
|
||||
integration = async_get_loaded_integration(hass, integration_domain)
|
||||
if not integration.is_built_in:
|
||||
return integration.issue_tracker
|
||||
|
||||
if module and "custom_components" in module:
|
||||
return None
|
||||
|
||||
if integration_domain:
|
||||
issue_tracker += f"+label%3A%22integration%3A+{integration_domain}%22"
|
||||
return issue_tracker
|
||||
|
||||
|
||||
@callback
|
||||
def async_suggest_report_issue(
|
||||
hass: HomeAssistant | None,
|
||||
*,
|
||||
integration_domain: str | None = None,
|
||||
module: str | None = None,
|
||||
) -> str:
|
||||
"""Generate a blurb asking the user to file a bug report."""
|
||||
issue_tracker = async_get_issue_tracker(
|
||||
hass, integration_domain=integration_domain, module=module
|
||||
)
|
||||
|
||||
if not issue_tracker:
|
||||
if not integration_domain:
|
||||
return "report it to the custom integration author"
|
||||
return (
|
||||
f"report it to the author of the '{integration_domain}' "
|
||||
"custom integration"
|
||||
)
|
||||
|
||||
return f"create a bug report at {issue_tracker}"
|
||||
|
|
|
@ -166,7 +166,7 @@ async def test_deprecated_last_reset(
|
|||
f"with state_class {state_class} has set last_reset. Setting last_reset for "
|
||||
"entities with state_class other than 'total' is not supported. Please update "
|
||||
"your configuration if state_class is manually configured, otherwise report it "
|
||||
"to the custom integration author"
|
||||
"to the author of the 'test' custom integration"
|
||||
) in caplog.text
|
||||
|
||||
state = hass.states.get("sensor.test")
|
||||
|
|
|
@ -776,9 +776,10 @@ async def test_warn_slow_write_state_custom_component(
|
|||
mock_entity.async_write_ha_state()
|
||||
|
||||
assert (
|
||||
"Updating state for comp_test.test_entity "
|
||||
"(<class 'custom_components.bla.sensor.test_warn_slow_write_state_custom_component.<locals>.CustomComponentEntity'>) "
|
||||
"took 10.000 seconds. Please report it to the custom integration author"
|
||||
"Updating state for comp_test.test_entity (<class 'custom_components.bla.sensor"
|
||||
".test_warn_slow_write_state_custom_component.<locals>.CustomComponentEntity'>)"
|
||||
" took 10.000 seconds. Please report it to the author of the 'hue' custom "
|
||||
"integration"
|
||||
) in caplog.text
|
||||
|
||||
|
||||
|
|
|
@ -744,3 +744,132 @@ async def test_loggers(hass: HomeAssistant) -> None:
|
|||
},
|
||||
)
|
||||
assert integration.loggers == ["name1", "name2"]
|
||||
|
||||
|
||||
CORE_ISSUE_TRACKER = (
|
||||
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
||||
)
|
||||
CORE_ISSUE_TRACKER_BUILT_IN = (
|
||||
CORE_ISSUE_TRACKER + "+label%3A%22integration%3A+bla_built_in%22"
|
||||
)
|
||||
CORE_ISSUE_TRACKER_CUSTOM = (
|
||||
CORE_ISSUE_TRACKER + "+label%3A%22integration%3A+bla_custom%22"
|
||||
)
|
||||
CORE_ISSUE_TRACKER_CUSTOM_NO_TRACKER = (
|
||||
CORE_ISSUE_TRACKER + "+label%3A%22integration%3A+bla_custom_no_tracker%22"
|
||||
)
|
||||
CORE_ISSUE_TRACKER_HUE = CORE_ISSUE_TRACKER + "+label%3A%22integration%3A+hue%22"
|
||||
CUSTOM_ISSUE_TRACKER = "https://blablabla.com"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "module", "issue_tracker"),
|
||||
[
|
||||
# If no information is available, open issue on core
|
||||
(None, None, CORE_ISSUE_TRACKER),
|
||||
("hue", "homeassistant.components.hue.sensor", CORE_ISSUE_TRACKER_HUE),
|
||||
("hue", None, CORE_ISSUE_TRACKER_HUE),
|
||||
("bla_built_in", None, CORE_ISSUE_TRACKER_BUILT_IN),
|
||||
# Integration domain is not currently deduced from module
|
||||
(None, "homeassistant.components.hue.sensor", CORE_ISSUE_TRACKER),
|
||||
("hue", "homeassistant.components.mqtt.sensor", CORE_ISSUE_TRACKER_HUE),
|
||||
# Custom integration with known issue tracker
|
||||
("bla_custom", "custom_components.bla_custom.sensor", CUSTOM_ISSUE_TRACKER),
|
||||
("bla_custom", None, CUSTOM_ISSUE_TRACKER),
|
||||
# Custom integration without known issue tracker
|
||||
(None, "custom_components.bla.sensor", None),
|
||||
("bla_custom_no_tracker", "custom_components.bla_custom.sensor", None),
|
||||
("bla_custom_no_tracker", None, None),
|
||||
("hue", "custom_components.bla.sensor", None),
|
||||
# Integration domain has priority over module
|
||||
("bla_custom_no_tracker", "homeassistant.components.bla_custom.sensor", None),
|
||||
],
|
||||
)
|
||||
async def test_async_get_issue_tracker(
|
||||
hass, domain: str | None, module: str | None, issue_tracker: str | None
|
||||
) -> None:
|
||||
"""Test async_get_issue_tracker."""
|
||||
mock_integration(hass, MockModule("bla_built_in"))
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"bla_custom", partial_manifest={"issue_tracker": CUSTOM_ISSUE_TRACKER}
|
||||
),
|
||||
built_in=False,
|
||||
)
|
||||
mock_integration(hass, MockModule("bla_custom_no_tracker"), built_in=False)
|
||||
assert (
|
||||
loader.async_get_issue_tracker(hass, integration_domain=domain, module=module)
|
||||
== issue_tracker
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "module", "issue_tracker"),
|
||||
[
|
||||
# If no information is available, open issue on core
|
||||
(None, None, CORE_ISSUE_TRACKER),
|
||||
("hue", "homeassistant.components.hue.sensor", CORE_ISSUE_TRACKER_HUE),
|
||||
("hue", None, CORE_ISSUE_TRACKER_HUE),
|
||||
("bla_built_in", None, CORE_ISSUE_TRACKER_BUILT_IN),
|
||||
# Integration domain is not currently deduced from module
|
||||
(None, "homeassistant.components.hue.sensor", CORE_ISSUE_TRACKER),
|
||||
("hue", "homeassistant.components.mqtt.sensor", CORE_ISSUE_TRACKER_HUE),
|
||||
# Custom integration with known issue tracker - can't find it without hass
|
||||
("bla_custom", "custom_components.bla_custom.sensor", None),
|
||||
# Assumed to be a core integration without hass and without module
|
||||
("bla_custom", None, CORE_ISSUE_TRACKER_CUSTOM),
|
||||
],
|
||||
)
|
||||
async def test_async_get_issue_tracker_no_hass(
|
||||
hass, domain: str | None, module: str | None, issue_tracker: str
|
||||
) -> None:
|
||||
"""Test async_get_issue_tracker."""
|
||||
mock_integration(hass, MockModule("bla_built_in"))
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"bla_custom", partial_manifest={"issue_tracker": CUSTOM_ISSUE_TRACKER}
|
||||
),
|
||||
built_in=False,
|
||||
)
|
||||
assert (
|
||||
loader.async_get_issue_tracker(None, integration_domain=domain, module=module)
|
||||
== issue_tracker
|
||||
)
|
||||
|
||||
|
||||
REPORT_CUSTOM = (
|
||||
"report it to the author of the 'bla_custom_no_tracker' custom integration"
|
||||
)
|
||||
REPORT_CUSTOM_UNKNOWN = "report it to the custom integration author"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("domain", "module", "report_issue"),
|
||||
[
|
||||
(None, None, f"create a bug report at {CORE_ISSUE_TRACKER}"),
|
||||
("bla_custom", None, f"create a bug report at {CUSTOM_ISSUE_TRACKER}"),
|
||||
("bla_custom_no_tracker", None, REPORT_CUSTOM),
|
||||
(None, "custom_components.hue.sensor", REPORT_CUSTOM_UNKNOWN),
|
||||
],
|
||||
)
|
||||
async def test_async_suggest_report_issue(
|
||||
hass, domain: str | None, module: str | None, report_issue: str
|
||||
) -> None:
|
||||
"""Test async_suggest_report_issue."""
|
||||
mock_integration(hass, MockModule("bla_built_in"))
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"bla_custom", partial_manifest={"issue_tracker": CUSTOM_ISSUE_TRACKER}
|
||||
),
|
||||
built_in=False,
|
||||
)
|
||||
mock_integration(hass, MockModule("bla_custom_no_tracker"), built_in=False)
|
||||
assert (
|
||||
loader.async_suggest_report_issue(
|
||||
hass, integration_domain=domain, module=module
|
||||
)
|
||||
== report_issue
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue