From 0ba339e56c72acdb0dea2a528d511b1348fd4a59 Mon Sep 17 00:00:00 2001 From: Matthias Alphart <farmio@alphart.net> Date: Wed, 12 Apr 2023 09:58:27 +0200 Subject: [PATCH] Run `socket.gethostbyname` in executor in Obihai and Sonos (#91190) * Run in executor in Obihai and Sonos * fix Sonos test * fix sonos test differently (review) --- homeassistant/components/obihai/config_flow.py | 6 ++++-- homeassistant/components/sonos/__init__.py | 14 ++++---------- homeassistant/components/sonos/helpers.py | 7 +++++++ tests/components/sonos/test_init.py | 12 ++++++------ 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/obihai/config_flow.py b/homeassistant/components/obihai/config_flow.py index 1a7d29fadba..6216fe0b973 100644 --- a/homeassistant/components/obihai/config_flow.py +++ b/homeassistant/components/obihai/config_flow.py @@ -66,7 +66,9 @@ class ObihaiFlowHandler(ConfigFlow, domain=DOMAIN): if user_input is not None: try: - ip = gethostbyname(user_input[CONF_HOST]) + ip = await self.hass.async_add_executor_job( + gethostbyname, user_input[CONF_HOST] + ) except gaierror: errors["base"] = "cannot_connect" @@ -139,7 +141,7 @@ class ObihaiFlowHandler(ConfigFlow, domain=DOMAIN): """Handle a flow initialized by importing a config.""" try: - _ = gethostbyname(config[CONF_HOST]) + _ = await self.hass.async_add_executor_job(gethostbyname, config[CONF_HOST]) except gaierror: return self.async_abort(reason="cannot_connect") diff --git a/homeassistant/components/sonos/__init__.py b/homeassistant/components/sonos/__init__.py index 4b68030f843..c17e4b00829 100644 --- a/homeassistant/components/sonos/__init__.py +++ b/homeassistant/components/sonos/__init__.py @@ -48,6 +48,7 @@ from .const import ( ) from .exception import SonosUpdateError from .favorites import SonosFavorites +from .helpers import sync_get_visible_zones from .speaker import SonosSpeaker _LOGGER = logging.getLogger(__name__) @@ -338,19 +339,12 @@ class SonosDiscoveryManager: self, now: datetime.datetime | None = None ) -> None: """Add and maintain Sonos devices from a manual configuration.""" - - def get_sync_attributes(soco: SoCo) -> set[SoCo]: - """Ensure I/O attributes are cached and return visible zones.""" - _ = soco.household_id - _ = soco.uid - return soco.visible_zones - for host in self.hosts: - ip_addr = socket.gethostbyname(host) + ip_addr = await self.hass.async_add_executor_job(socket.gethostbyname, host) soco = SoCo(ip_addr) try: visible_zones = await self.hass.async_add_executor_job( - get_sync_attributes, + sync_get_visible_zones, soco, ) except (OSError, SoCoException, Timeout) as ex: @@ -382,7 +376,7 @@ class SonosDiscoveryManager: break for host in self.hosts.copy(): - ip_addr = socket.gethostbyname(host) + ip_addr = await self.hass.async_add_executor_job(socket.gethostbyname, host) if self.is_device_invisible(ip_addr): _LOGGER.debug("Discarding %s from manual hosts", ip_addr) self.hosts.discard(ip_addr) diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index 5f44b9bae6f..1005b6c7d6a 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -117,3 +117,10 @@ def hostname_to_uid(hostname: str) -> str: else: raise ValueError(f"{hostname} is not a sonos device.") return f"{UID_PREFIX}{baseuid}{UID_POSTFIX}" + + +def sync_get_visible_zones(soco: SoCo) -> set[SoCo]: + """Ensure I/O attributes are cached and return visible zones.""" + _ = soco.household_id + _ = soco.uid + return soco.visible_zones diff --git a/tests/components/sonos/test_init.py b/tests/components/sonos/test_init.py index 6cc79e1b2f0..596946e9f8b 100644 --- a/tests/components/sonos/test_init.py +++ b/tests/components/sonos/test_init.py @@ -1,6 +1,6 @@ """Tests for the Sonos config flow.""" import logging -from unittest.mock import AsyncMock, patch +from unittest.mock import patch import pytest @@ -86,16 +86,16 @@ async def test_async_poll_manual_hosts_warnings( manager, "_async_handle_discovery_message" ), patch("homeassistant.components.sonos.async_call_later"), patch( "homeassistant.components.sonos.async_dispatcher_send" - ), patch.object( - hass, "async_add_executor_job", new=AsyncMock() - ) as mock_async_add_executor_job: - mock_async_add_executor_job.side_effect = [ + ), patch( + "homeassistant.components.sonos.sync_get_visible_zones", + side_effect=[ OSError(), OSError(), [], [], OSError(), - ] + ], + ): # First call fails, it should be logged as a WARNING message caplog.clear() await manager.async_poll_manual_hosts() -- GitLab