From aea1209d0d7461fe6eaff5367937f483d7689bc6 Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 28 Feb 2022 18:38:08 -0600 Subject: [PATCH] Reduce magic in Sonos error handling fixture (#67401) --- homeassistant/components/sonos/helpers.py | 32 ++++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index fbc1d2642ea..a11847d2b0c 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable import logging -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar from soco import SoCo from soco.exceptions import SoCoException, SoCoUPnPException @@ -55,15 +55,9 @@ def soco_error( ) return None - # In order of preference: - # * SonosSpeaker instance - # * SoCo instance passed as an arg - # * SoCo instance (as self) - speaker_or_soco = getattr(self, "speaker", args_soco or self) - zone_name = speaker_or_soco.zone_name - # Prefer the entity_id if available, zone name as a fallback - # Needed as SonosSpeaker instances are not entities - target = getattr(self, "entity_id", zone_name) + if (target := _find_target_identifier(self, args_soco)) is None: + raise RuntimeError("Unexpected use of soco_error") from err + message = f"Error calling {function} on {target}: {err}" raise SonosUpdateError(message) from err @@ -80,6 +74,24 @@ def soco_error( return decorator +def _find_target_identifier(instance: Any, fallback_soco: SoCo | None) -> str | None: + """Extract the the best available target identifier from the provided instance object.""" + if entity_id := getattr(instance, "entity_id", None): + # SonosEntity instance + return entity_id + if zone_name := getattr(instance, "zone_name", None): + # SonosSpeaker instance + return zone_name + if speaker := getattr(instance, "speaker", None): + # Holds a SonosSpeaker instance attribute + return speaker.zone_name + if soco := getattr(instance, "soco", fallback_soco): + # Holds a SoCo instance attribute + # Only use attributes with no I/O + return soco._player_name or soco.ip_address # pylint: disable=protected-access + return None + + def hostname_to_uid(hostname: str) -> str: """Convert a Sonos hostname to a uid.""" if hostname.startswith("Sonos-"):