From 4e4345f04e7b4570bbd5dae7d68e20d104df929e Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Wed, 28 Feb 2024 00:25:46 +0100 Subject: [PATCH] Drop `@bind_hass` use from hassio component (#111522) * Drop `@bind_hass` use from hassio component * Add comment why we import locally --------- Co-authored-by: J. Nick Koston --- homeassistant/components/hassio/__init__.py | 14 ------------- homeassistant/components/hassio/handler.py | 16 -------------- homeassistant/helpers/network.py | 13 +++++++++--- homeassistant/helpers/system_info.py | 12 ++++++----- tests/components/hassio/test_init.py | 8 ++++--- tests/helpers/test_network.py | 23 ++++++++++++--------- 6 files changed, 35 insertions(+), 51 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 744754a31a8..fc463424972 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -41,7 +41,6 @@ from homeassistant.helpers.event import async_call_later from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from homeassistant.loader import bind_hass from homeassistant.util.dt import now from .addon_manager import AddonError, AddonInfo, AddonManager, AddonState # noqa: F401 @@ -283,7 +282,6 @@ def hostname_from_addon_slug(addon_slug: str) -> str: @callback -@bind_hass def get_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic information from Supervisor. @@ -293,7 +291,6 @@ def get_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_host_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic host information. @@ -303,7 +300,6 @@ def get_host_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_store(hass: HomeAssistant) -> dict[str, Any] | None: """Return store information. @@ -313,7 +309,6 @@ def get_store(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Supervisor information. @@ -323,7 +318,6 @@ def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None: """Return Addons info. @@ -333,7 +327,6 @@ def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None: @callback -@bind_hass def get_addons_stats(hass: HomeAssistant) -> dict[str, Any]: """Return Addons stats. @@ -343,7 +336,6 @@ def get_addons_stats(hass: HomeAssistant) -> dict[str, Any]: @callback -@bind_hass def get_core_stats(hass: HomeAssistant) -> dict[str, Any]: """Return core stats. @@ -353,7 +345,6 @@ def get_core_stats(hass: HomeAssistant) -> dict[str, Any]: @callback -@bind_hass def get_supervisor_stats(hass: HomeAssistant) -> dict[str, Any]: """Return supervisor stats. @@ -363,7 +354,6 @@ def get_supervisor_stats(hass: HomeAssistant) -> dict[str, Any]: @callback -@bind_hass def get_addons_changelogs(hass: HomeAssistant): """Return Addons changelogs. @@ -373,7 +363,6 @@ def get_addons_changelogs(hass: HomeAssistant): @callback -@bind_hass def get_os_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return OS information. @@ -383,7 +372,6 @@ def get_os_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_core_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Home Assistant Core information from Supervisor. @@ -393,7 +381,6 @@ def get_core_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback -@bind_hass def get_issues_info(hass: HomeAssistant) -> SupervisorIssues | None: """Return Supervisor issues info. @@ -403,7 +390,6 @@ def get_issues_info(hass: HomeAssistant) -> SupervisorIssues | None: @callback -@bind_hass def is_hassio(hass: HomeAssistant) -> bool: """Return true if Hass.io is loaded. diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 9b8e6367647..29b70abdd11 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -19,7 +19,6 @@ from homeassistant.components.http import ( ) from homeassistant.const import SERVER_PORT from homeassistant.core import HomeAssistant -from homeassistant.loader import bind_hass from .const import ATTR_DISCOVERY, DOMAIN, X_HASS_SOURCE @@ -63,7 +62,6 @@ def api_data( return _wrapper -@bind_hass async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict: """Return add-on info. @@ -85,7 +83,6 @@ async def async_get_addon_store_info(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, method="get") -@bind_hass async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bool: """Update Supervisor diagnostics toggle. @@ -95,7 +92,6 @@ async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bo return await hassio.update_diagnostics(diagnostics) -@bind_hass @api_data async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: """Install add-on. @@ -107,7 +103,6 @@ async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=None) -@bind_hass @api_data async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: """Uninstall add-on. @@ -119,7 +114,6 @@ async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) -@bind_hass @api_data async def async_update_addon( hass: HomeAssistant, @@ -139,7 +133,6 @@ async def async_update_addon( ) -@bind_hass @api_data async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: """Start add-on. @@ -151,7 +144,6 @@ async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) -@bind_hass @api_data async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: """Restart add-on. @@ -163,7 +155,6 @@ async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=None) -@bind_hass @api_data async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: """Stop add-on. @@ -175,7 +166,6 @@ async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) -@bind_hass @api_data async def async_set_addon_options( hass: HomeAssistant, slug: str, options: dict @@ -189,7 +179,6 @@ async def async_set_addon_options( return await hassio.send_command(command, payload=options) -@bind_hass async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict | None: """Return discovery data for an add-on.""" hassio: HassIO = hass.data[DOMAIN] @@ -198,7 +187,6 @@ async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict return next((addon for addon in discovered_addons if addon["addon"] == slug), None) -@bind_hass @api_data async def async_create_backup( hass: HomeAssistant, payload: dict, partial: bool = False @@ -213,7 +201,6 @@ async def async_create_backup( return await hassio.send_command(command, payload=payload, timeout=None) -@bind_hass @api_data async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict: """Update Home Assistant Operating System. @@ -229,7 +216,6 @@ async def async_update_os(hass: HomeAssistant, version: str | None = None) -> di ) -@bind_hass @api_data async def async_update_supervisor(hass: HomeAssistant) -> dict: """Update Home Assistant Supervisor. @@ -241,7 +227,6 @@ async def async_update_supervisor(hass: HomeAssistant) -> dict: return await hassio.send_command(command, timeout=None) -@bind_hass @api_data async def async_update_core( hass: HomeAssistant, version: str | None = None, backup: bool = False @@ -259,7 +244,6 @@ async def async_update_core( ) -@bind_hass @_api_bool async def async_apply_suggestion(hass: HomeAssistant, suggestion_uuid: str) -> dict: """Apply a suggestion from supervisor's resolution center. diff --git a/homeassistant/helpers/network.py b/homeassistant/helpers/network.py index 58ca191feb0..08d6dfe021e 100644 --- a/homeassistant/helpers/network.py +++ b/homeassistant/helpers/network.py @@ -40,7 +40,11 @@ def get_supervisor_network_url( hass: HomeAssistant, *, allow_ssl: bool = False ) -> str | None: """Get URL for home assistant within supervisor network.""" - if hass.config.api is None or not hass.components.hassio.is_hassio(): + # Local import to avoid circular dependencies + # pylint: disable-next=import-outside-toplevel + from homeassistant.components.hassio import is_hassio + + if hass.config.api is None or not is_hassio(hass): return None scheme = "http" @@ -125,6 +129,10 @@ def get_url( prefer_cloud: bool = False, ) -> str: """Get a URL to this instance.""" + # Local import to avoid circular dependencies + # pylint: disable-next=import-outside-toplevel + from homeassistant.components.hassio import get_host_info, is_hassio + if require_current_request and http.current_request.get() is None: raise NoURLAvailableError @@ -176,8 +184,7 @@ def get_url( ) known_hostnames = ["localhost"] - if hass.components.hassio.is_hassio(): - host_info = hass.components.hassio.get_host_info() + if is_hassio(hass) and (host_info := get_host_info(hass)): known_hostnames.extend( [host_info["hostname"], f"{host_info['hostname']}.local"] ) diff --git a/homeassistant/helpers/system_info.py b/homeassistant/helpers/system_info.py index 8af04c11c18..d08db3f981a 100644 --- a/homeassistant/helpers/system_info.py +++ b/homeassistant/helpers/system_info.py @@ -10,7 +10,6 @@ from typing import Any from homeassistant.const import __version__ as current_version from homeassistant.core import HomeAssistant -from homeassistant.loader import bind_hass from homeassistant.util.package import is_docker_env, is_virtual_env _LOGGER = logging.getLogger(__name__) @@ -27,10 +26,13 @@ def is_official_image() -> bool: cached_get_user = cache(getuser) -@bind_hass async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]: """Return info about the system.""" - is_hassio = hass.components.hassio.is_hassio() + # Local import to avoid circular dependencies + # pylint: disable-next=import-outside-toplevel + from homeassistant.components import hassio + + is_hassio = hassio.is_hassio(hass) info_object = { "installation_type": "Unknown", @@ -68,11 +70,11 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]: # Enrich with Supervisor information if is_hassio: - if not (info := hass.components.hassio.get_info()): + if not (info := hassio.get_info(hass)): _LOGGER.warning("No Home Assistant Supervisor info available") info = {} - host = hass.components.hassio.get_host_info() or {} + host = hassio.get_host_info(hass) or {} info_object["supervisor"] = info.get("supervisor") info_object["host_os"] = host.get("operating_system") info_object["docker_version"] = info.get("docker") diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 7d0943b3677..a6f94152af0 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -15,7 +15,9 @@ from homeassistant.components.hassio import ( DOMAIN, STORAGE_KEY, async_get_addon_store_info, + get_core_info, hostname_from_addon_slug, + is_hassio, ) from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY from homeassistant.components.hassio.handler import HassioAPIError @@ -246,8 +248,8 @@ async def test_setup_api_ping( assert result assert aioclient_mock.call_count == 19 - assert hass.components.hassio.get_core_info()["version_latest"] == "1.0.0" - assert hass.components.hassio.is_hassio() + assert get_core_info(hass)["version_latest"] == "1.0.0" + assert is_hassio(hass) async def test_setup_api_panel( @@ -465,7 +467,7 @@ async def test_warn_when_cannot_connect( result = await async_setup_component(hass, "hassio", {}) assert result - assert hass.components.hassio.is_hassio() + assert is_hassio(hass) assert "Not connected with the supervisor / system too busy!" in caplog.text diff --git a/tests/helpers/test_network.py b/tests/helpers/test_network.py index b8fbfc9346b..b697cd4373e 100644 --- a/tests/helpers/test_network.py +++ b/tests/helpers/test_network.py @@ -32,6 +32,16 @@ def mock_current_request_mock(): yield mock_current_request +@pytest.fixture +def patch_hassio(): + """Patch the hassio component.""" + with patch("homeassistant.components.hassio.is_hassio", return_value=True), patch( + "homeassistant.components.hassio.get_host_info", + return_value={"hostname": "homeassistant"}, + ): + yield + + async def test_get_url_internal(hass: HomeAssistant) -> None: """Test getting an instance URL when the user has set an internal URL.""" assert hass.config.internal_url is None @@ -566,7 +576,7 @@ async def test_get_request_host(hass: HomeAssistant) -> None: async def test_get_current_request_url_with_known_host( - hass: HomeAssistant, current_request + hass: HomeAssistant, current_request, patch_hassio ) -> None: """Test getting current request URL with known hosts addresses.""" hass.config.api = Mock(use_ssl=False, port=8123, local_ip="127.0.0.1") @@ -595,10 +605,6 @@ async def test_get_current_request_url_with_known_host( # Ensure hostname from Supervisor is accepted transparently mock_component(hass, "hassio") - hass.components.hassio.is_hassio = Mock(return_value=True) - hass.components.hassio.get_host_info = Mock( - return_value={"hostname": "homeassistant"} - ) with patch( "homeassistant.helpers.network._get_request_host", @@ -662,11 +668,8 @@ async def test_is_internal_request(hass: HomeAssistant, mock_current_request) -> assert is_internal_request(hass), mock_current_request.return_value.url # Test for matching against HassOS hostname - with patch.object( - hass.components.hassio, "is_hassio", return_value=True - ), patch.object( - hass.components.hassio, - "get_host_info", + with patch("homeassistant.components.hassio.is_hassio", return_value=True), patch( + "homeassistant.components.hassio.get_host_info", return_value={"hostname": "hellohost"}, ): for allowed in ("hellohost", "hellohost.local"):