From c0e3ccdb83c76a007e4c3b3412d354c4cf74b4f0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 22 Aug 2019 12:48:02 +0200 Subject: [PATCH] Improve gdbus error handling (#1252) * Improve gdbus error handling * Fix logging type * Detect no dbus * Fix issue with complex * Update hassio/dbus/__init__.py Co-Authored-By: Franck Nijhof * Update hassio/dbus/hostname.py Co-Authored-By: Franck Nijhof * Update hassio/dbus/rauc.py Co-Authored-By: Franck Nijhof * Update hassio/dbus/systemd.py Co-Authored-By: Franck Nijhof * Fix black --- hassio/__main__.py | 2 +- hassio/addons/__init__.py | 2 +- hassio/addons/addon.py | 2 +- hassio/addons/data.py | 2 +- hassio/addons/utils.py | 2 +- hassio/addons/validate.py | 2 +- hassio/api/__init__.py | 2 +- hassio/api/addons.py | 2 +- hassio/api/auth.py | 2 +- hassio/api/dns.py | 2 +- hassio/api/hardware.py | 2 +- hassio/api/hassos.py | 2 +- hassio/api/homeassistant.py | 2 +- hassio/api/host.py | 2 +- hassio/api/info.py | 2 +- hassio/api/ingress.py | 2 +- hassio/api/proxy.py | 2 +- hassio/api/security.py | 2 +- hassio/api/snapshots.py | 2 +- hassio/api/supervisor.py | 2 +- hassio/api/utils.py | 2 +- hassio/arch.py | 2 +- hassio/auth.py | 2 +- hassio/bootstrap.py | 2 +- hassio/config.py | 2 +- hassio/core.py | 2 +- hassio/dbus/__init__.py | 36 ++-- hassio/dbus/hostname.py | 8 +- hassio/dbus/rauc.py | 6 +- hassio/dbus/systemd.py | 8 +- hassio/discovery/__init__.py | 2 +- hassio/dns.py | 2 +- hassio/docker/__init__.py | 2 +- hassio/docker/addon.py | 2 +- hassio/docker/dns.py | 2 +- hassio/docker/hassos_cli.py | 2 +- hassio/docker/homeassistant.py | 2 +- hassio/docker/interface.py | 2 +- hassio/docker/network.py | 2 +- hassio/docker/supervisor.py | 2 +- hassio/exceptions.py | 4 + hassio/hassos.py | 2 +- hassio/homeassistant.py | 2 +- hassio/host/__init__.py | 2 +- hassio/host/alsa.py | 2 +- hassio/host/apparmor.py | 2 +- hassio/host/control.py | 2 +- hassio/host/info.py | 2 +- hassio/host/services.py | 2 +- hassio/ingress.py | 2 +- hassio/misc/forwarder.py | 2 +- hassio/misc/hardware.py | 2 +- hassio/misc/scheduler.py | 2 +- hassio/services/modules/mqtt.py | 2 +- hassio/snapshots/__init__.py | 2 +- hassio/snapshots/snapshot.py | 2 +- hassio/store/__init__.py | 2 +- hassio/store/addon.py | 2 +- hassio/store/data.py | 2 +- hassio/store/git.py | 2 +- hassio/store/utils.py | 2 +- hassio/supervisor.py | 2 +- hassio/tasks.py | 2 +- hassio/updater.py | 2 +- hassio/utils/__init__.py | 2 +- hassio/utils/apparmor.py | 2 +- hassio/utils/dt.py | 2 +- hassio/utils/gdbus.py | 137 ++++++++------ hassio/utils/json.py | 2 +- tests/utils/test_gvariant_parser.py | 278 ++++++++++++++++++++++++++++ 70 files changed, 467 insertions(+), 136 deletions(-) create mode 100644 tests/utils/test_gvariant_parser.py diff --git a/hassio/__main__.py b/hassio/__main__.py index baed3dda8..426845fa4 100644 --- a/hassio/__main__.py +++ b/hassio/__main__.py @@ -6,7 +6,7 @@ import sys from hassio import bootstrap -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) def initialize_event_loop(): diff --git a/hassio/addons/__init__.py b/hassio/addons/__init__.py index 7c0b6df89..dba02f21f 100644 --- a/hassio/addons/__init__.py +++ b/hassio/addons/__init__.py @@ -19,7 +19,7 @@ from ..store.addon import AddonStore from .addon import Addon from .data import AddonsData -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) AnyAddon = Union[Addon, AddonStore] diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index 28be30793..375ad52a7 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -55,7 +55,7 @@ from .model import AddonModel, Data from .utils import remove_data from .validate import SCHEMA_ADDON_SNAPSHOT, validate_options -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) RE_WEBUI = re.compile( r"^(?:(?Phttps?)|\[PROTO:(?P\w+)\])" diff --git a/hassio/addons/data.py b/hassio/addons/data.py index eb8a99bb5..f94e2af95 100644 --- a/hassio/addons/data.py +++ b/hassio/addons/data.py @@ -17,7 +17,7 @@ from ..store.addon import AddonStore from .addon import Addon from .validate import SCHEMA_ADDONS_FILE -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) Config = Dict[str, Any] diff --git a/hassio/addons/utils.py b/hassio/addons/utils.py index 562399222..fde882213 100644 --- a/hassio/addons/utils.py +++ b/hassio/addons/utils.py @@ -22,7 +22,7 @@ from ..const import ( if TYPE_CHECKING: from .model import AddonModel -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) def rating_security(addon: AddonModel) -> int: diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index 6ea3911c1..1c65b92bb 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -95,7 +95,7 @@ from ..validate import ( UUID_MATCH, ) -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) RE_VOLUME = re.compile(r"^(config|ssl|addons|backup|share)(?::(rw|ro))?$") diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 2a02eaeb8..628d7795d 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -22,7 +22,7 @@ from .services import APIServices from .snapshots import APISnapshots from .supervisor import APISupervisor -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class RestAPI(CoreSysAttributes): diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 978034a93..6ee425192 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -94,7 +94,7 @@ from ..exceptions import APIError from ..validate import ALSA_DEVICE, DOCKER_PORTS from .utils import api_process, api_process_raw, api_validate -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)}) diff --git a/hassio/api/auth.py b/hassio/api/auth.py index 1810d01fa..6c33aab63 100644 --- a/hassio/api/auth.py +++ b/hassio/api/auth.py @@ -10,7 +10,7 @@ from ..const import REQUEST_FROM, CONTENT_TYPE_JSON, CONTENT_TYPE_URL from ..coresys import CoreSysAttributes from ..exceptions import APIForbidden -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class APIAuth(CoreSysAttributes): diff --git a/hassio/api/dns.py b/hassio/api/dns.py index b4666b9eb..caef4c8fa 100644 --- a/hassio/api/dns.py +++ b/hassio/api/dns.py @@ -26,7 +26,7 @@ from ..exceptions import APIError from ..validate import DNS_SERVER_LIST from .utils import api_process, api_process_raw, api_validate -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_SERVERS): DNS_SERVER_LIST}) diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py index 80bcc35bc..f9fa125a3 100644 --- a/hassio/api/hardware.py +++ b/hassio/api/hardware.py @@ -12,7 +12,7 @@ from ..const import ( ) from ..coresys import CoreSysAttributes -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class APIHardware(CoreSysAttributes): diff --git a/hassio/api/hassos.py b/hassio/api/hassos.py index 36a84c481..78e853707 100644 --- a/hassio/api/hassos.py +++ b/hassio/api/hassos.py @@ -16,7 +16,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from .utils import api_process, api_validate -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)}) diff --git a/hassio/api/homeassistant.py b/hassio/api/homeassistant.py index fad4f5424..ec68ebb5a 100644 --- a/hassio/api/homeassistant.py +++ b/hassio/api/homeassistant.py @@ -36,7 +36,7 @@ from ..exceptions import APIError from ..validate import DOCKER_IMAGE, NETWORK_PORT from .utils import api_process, api_process_raw, api_validate -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter SCHEMA_OPTIONS = vol.Schema( diff --git a/hassio/api/host.py b/hassio/api/host.py index 8b93fc652..6c05ea5b0 100644 --- a/hassio/api/host.py +++ b/hassio/api/host.py @@ -20,7 +20,7 @@ from ..const import ( ) from ..coresys import CoreSysAttributes -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) SERVICE = "service" diff --git a/hassio/api/info.py b/hassio/api/info.py index 249f6f03c..572552119 100644 --- a/hassio/api/info.py +++ b/hassio/api/info.py @@ -19,7 +19,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from .utils import api_process -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class APIInfo(CoreSysAttributes): diff --git a/hassio/api/ingress.py b/hassio/api/ingress.py index a5546b30c..e870a999b 100644 --- a/hassio/api/ingress.py +++ b/hassio/api/ingress.py @@ -28,7 +28,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from .utils import api_process -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class APIIngress(CoreSysAttributes): diff --git a/hassio/api/proxy.py b/hassio/api/proxy.py index c193aeb60..c0b310163 100644 --- a/hassio/api/proxy.py +++ b/hassio/api/proxy.py @@ -14,7 +14,7 @@ from ..const import HEADER_HA_ACCESS from ..coresys import CoreSysAttributes from ..exceptions import HomeAssistantAuthError, HomeAssistantAPIError, APIError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class APIProxy(CoreSysAttributes): diff --git a/hassio/api/security.py b/hassio/api/security.py index 7346097db..bdc0b3bc3 100644 --- a/hassio/api/security.py +++ b/hassio/api/security.py @@ -16,7 +16,7 @@ from ..const import ( ) from ..coresys import CoreSysAttributes -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # fmt: off diff --git a/hassio/api/snapshots.py b/hassio/api/snapshots.py index 41f692d60..e2529df52 100644 --- a/hassio/api/snapshots.py +++ b/hassio/api/snapshots.py @@ -28,7 +28,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from ..exceptions import APIError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter diff --git a/hassio/api/supervisor.py b/hassio/api/supervisor.py index 66c6bb632..d266f4b2b 100644 --- a/hassio/api/supervisor.py +++ b/hassio/api/supervisor.py @@ -44,7 +44,7 @@ from ..utils.validate import validate_timezone from ..validate import CHANNELS, LOG_LEVEL, REPOSITORIES, WAIT_BOOT from .utils import api_process, api_process_raw, api_validate -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter SCHEMA_OPTIONS = vol.Schema( diff --git a/hassio/api/utils.py b/hassio/api/utils.py index 3a6f435d1..9cbd3fb99 100644 --- a/hassio/api/utils.py +++ b/hassio/api/utils.py @@ -16,7 +16,7 @@ from ..const import ( ) from ..exceptions import HassioError, APIError, APIForbidden -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) def json_loads(data): diff --git a/hassio/arch.py b/hassio/arch.py index fccb76a1f..6fea24c88 100644 --- a/hassio/arch.py +++ b/hassio/arch.py @@ -8,7 +8,7 @@ from .coresys import CoreSys, CoreSysAttributes from .exceptions import HassioArchNotFound, JsonFileError from .utils.json import read_json_file -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) ARCH_JSON: Path = Path(__file__).parent.joinpath("data/arch.json") diff --git a/hassio/auth.py b/hassio/auth.py index e88dbe2c9..5d932a17a 100644 --- a/hassio/auth.py +++ b/hassio/auth.py @@ -8,7 +8,7 @@ from .utils.json import JsonConfig from .validate import SCHEMA_AUTH_CONFIG from .exceptions import AuthError, HomeAssistantAPIError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class Auth(JsonConfig, CoreSysAttributes): diff --git a/hassio/bootstrap.py b/hassio/bootstrap.py index f4f0d8bdc..348fdcf18 100644 --- a/hassio/bootstrap.py +++ b/hassio/bootstrap.py @@ -29,7 +29,7 @@ from .tasks import Tasks from .updater import Updater from .utils.dt import fetch_timezone -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) ENV_SHARE = "SUPERVISOR_SHARE" ENV_NAME = "SUPERVISOR_NAME" diff --git a/hassio/config.py b/hassio/config.py index a00b741e3..7d6161c17 100644 --- a/hassio/config.py +++ b/hassio/config.py @@ -19,7 +19,7 @@ from .utils.dt import parse_datetime from .utils.json import JsonConfig from .validate import SCHEMA_HASSIO_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) HOMEASSISTANT_CONFIG = PurePath("homeassistant") diff --git a/hassio/core.py b/hassio/core.py index 4b0027175..5e501d7c2 100644 --- a/hassio/core.py +++ b/hassio/core.py @@ -14,7 +14,7 @@ from .const import ( ) from .exceptions import HassioError, HomeAssistantError, SupervisorUpdateError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class HassIO(CoreSysAttributes): diff --git a/hassio/dbus/__init__.py b/hassio/dbus/__init__.py index 20941d566..6b88e2252 100644 --- a/hassio/dbus/__init__.py +++ b/hassio/dbus/__init__.py @@ -1,39 +1,49 @@ """D-Bus interface objects.""" +import logging from .systemd import Systemd from .hostname import Hostname from .rauc import Rauc -from ..coresys import CoreSysAttributes +from ..coresys import CoreSysAttributes, CoreSys +from ..exceptions import DBusNotConnectedError + +_LOGGER: logging.Logger = logging.getLogger(__name__) class DBusManager(CoreSysAttributes): """A DBus Interface handler.""" - def __init__(self, coresys): + def __init__(self, coresys: CoreSys) -> None: """Initialize D-Bus interface.""" - self.coresys = coresys + self.coresys: CoreSys = coresys - self._systemd = Systemd() - self._hostname = Hostname() - self._rauc = Rauc() + self._systemd: Systemd = Systemd() + self._hostname: Hostname = Hostname() + self._rauc: Rauc = Rauc() @property - def systemd(self): + def systemd(self) -> Systemd: """Return the systemd interface.""" return self._systemd @property - def hostname(self): + def hostname(self) -> Hostname: """Return the hostname interface.""" return self._hostname @property - def rauc(self): + def rauc(self) -> Rauc: """Return the rauc interface.""" return self._rauc - async def load(self): + async def load(self) -> None: """Connect interfaces to D-Bus.""" - await self.systemd.connect() - await self.hostname.connect() - await self.rauc.connect() + + try: + await self.systemd.connect() + await self.hostname.connect() + await self.rauc.connect() + except DBusNotConnectedError: + _LOGGER.error( + "No DBus support from Host. Disabled any kind of host control!" + ) diff --git a/hassio/dbus/hostname.py b/hassio/dbus/hostname.py index c78a5fb07..b63c1a4ea 100644 --- a/hassio/dbus/hostname.py +++ b/hassio/dbus/hostname.py @@ -3,10 +3,10 @@ import logging from .interface import DBusInterface from .utils import dbus_connected -from ..exceptions import DBusError +from ..exceptions import DBusError, DBusInterfaceError from ..utils.gdbus import DBus -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) DBUS_NAME = "org.freedesktop.hostname1" DBUS_OBJECT = "/org/freedesktop/hostname1" @@ -21,6 +21,10 @@ class Hostname(DBusInterface): self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT) except DBusError: _LOGGER.warning("Can't connect to hostname") + except DBusInterfaceError: + _LOGGER.warning( + "No hostname support on the host. Hostname functions have been disabled." + ) @dbus_connected def set_static_hostname(self, hostname): diff --git a/hassio/dbus/rauc.py b/hassio/dbus/rauc.py index 8ef6adc2d..7b2b35aad 100644 --- a/hassio/dbus/rauc.py +++ b/hassio/dbus/rauc.py @@ -3,10 +3,10 @@ import logging from .interface import DBusInterface from .utils import dbus_connected -from ..exceptions import DBusError +from ..exceptions import DBusError, DBusInterfaceError from ..utils.gdbus import DBus -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) DBUS_NAME = "de.pengutronix.rauc" DBUS_OBJECT = "/" @@ -21,6 +21,8 @@ class Rauc(DBusInterface): self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT) except DBusError: _LOGGER.warning("Can't connect to rauc") + except DBusInterfaceError: + _LOGGER.warning("Host has no rauc support. OTA updates have been disabled.") @dbus_connected def install(self, raucb_file): diff --git a/hassio/dbus/systemd.py b/hassio/dbus/systemd.py index fecaee3bb..e85d90dbb 100644 --- a/hassio/dbus/systemd.py +++ b/hassio/dbus/systemd.py @@ -3,10 +3,10 @@ import logging from .interface import DBusInterface from .utils import dbus_connected -from ..exceptions import DBusError +from ..exceptions import DBusError, DBusInterfaceError from ..utils.gdbus import DBus -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) DBUS_NAME = "org.freedesktop.systemd1" DBUS_OBJECT = "/org/freedesktop/systemd1" @@ -21,6 +21,10 @@ class Systemd(DBusInterface): self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT) except DBusError: _LOGGER.warning("Can't connect to systemd") + except DBusInterfaceError: + _LOGGER.warning( + "No systemd support on the host. Host control has been disabled." + ) @dbus_connected def reboot(self): diff --git a/hassio/discovery/__init__.py b/hassio/discovery/__init__.py index 0c0a17b68..7fc5bfb49 100644 --- a/hassio/discovery/__init__.py +++ b/hassio/discovery/__init__.py @@ -19,7 +19,7 @@ from .validate import SCHEMA_DISCOVERY_CONFIG, valid_discovery_config if TYPE_CHECKING: from ..addons.addon import Addon -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) CMD_NEW = "post" CMD_DEL = "delete" diff --git a/hassio/dns.py b/hassio/dns.py index a06bd81f0..92b362021 100644 --- a/hassio/dns.py +++ b/hassio/dns.py @@ -18,7 +18,7 @@ from .misc.forwarder import DNSForward from .utils.json import JsonConfig from .validate import SCHEMA_DNS_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) COREDNS_TMPL: Path = Path(__file__).parents[0].joinpath("data/coredns.tmpl") RESOLV_CONF: Path = Path("/etc/resolv.conf") diff --git a/hassio/docker/__init__.py b/hassio/docker/__init__.py index 13130df84..2ee5223d7 100644 --- a/hassio/docker/__init__.py +++ b/hassio/docker/__init__.py @@ -11,7 +11,7 @@ from ..const import SOCKET_DOCKER, DNS_SUFFIX from ..exceptions import DockerAPIError from .network import DockerNetwork -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) @attr.s(frozen=True) diff --git a/hassio/docker/addon.py b/hassio/docker/addon.py index 3b17208b8..c753ba783 100644 --- a/hassio/docker/addon.py +++ b/hassio/docker/addon.py @@ -32,7 +32,7 @@ if TYPE_CHECKING: from ..addons.addon import Addon -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) AUDIO_DEVICE = "/dev/snd:/dev/snd:rwm" NO_ADDDRESS = ip_address("0.0.0.0") diff --git a/hassio/docker/dns.py b/hassio/docker/dns.py index 5b29f64bd..c6d76cb47 100644 --- a/hassio/docker/dns.py +++ b/hassio/docker/dns.py @@ -7,7 +7,7 @@ from ..coresys import CoreSysAttributes from ..exceptions import DockerAPIError from .interface import DockerInterface -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) DNS_DOCKER_NAME: str = "hassio_dns" diff --git a/hassio/docker/hassos_cli.py b/hassio/docker/hassos_cli.py index e9413ea3f..f9eaa6977 100644 --- a/hassio/docker/hassos_cli.py +++ b/hassio/docker/hassos_cli.py @@ -6,7 +6,7 @@ import docker from ..coresys import CoreSysAttributes from .interface import DockerInterface -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class DockerHassOSCli(DockerInterface, CoreSysAttributes): diff --git a/hassio/docker/homeassistant.py b/hassio/docker/homeassistant.py index 7ab0f0c96..778c497bb 100644 --- a/hassio/docker/homeassistant.py +++ b/hassio/docker/homeassistant.py @@ -10,7 +10,7 @@ from ..const import ENV_TIME, ENV_TOKEN, LABEL_MACHINE from ..exceptions import DockerAPIError from .interface import CommandReturn, DockerInterface -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) HASS_DOCKER_NAME = "homeassistant" diff --git a/hassio/docker/interface.py b/hassio/docker/interface.py index 02f8c6485..12ad5e21d 100644 --- a/hassio/docker/interface.py +++ b/hassio/docker/interface.py @@ -13,7 +13,7 @@ from ..exceptions import DockerAPIError from ..utils import process_lock from .stats import DockerStats -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class DockerInterface(CoreSysAttributes): diff --git a/hassio/docker/network.py b/hassio/docker/network.py index 9a4ab7258..3f4d0943b 100644 --- a/hassio/docker/network.py +++ b/hassio/docker/network.py @@ -8,7 +8,7 @@ import docker from ..const import DOCKER_NETWORK, DOCKER_NETWORK_MASK, DOCKER_NETWORK_RANGE from ..exceptions import DockerAPIError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class DockerNetwork: diff --git a/hassio/docker/supervisor.py b/hassio/docker/supervisor.py index 15535eb7b..961fd1551 100644 --- a/hassio/docker/supervisor.py +++ b/hassio/docker/supervisor.py @@ -10,7 +10,7 @@ from ..coresys import CoreSysAttributes from ..exceptions import DockerAPIError from .interface import DockerInterface -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class DockerSupervisor(DockerInterface, CoreSysAttributes): diff --git a/hassio/exceptions.py b/hassio/exceptions.py index b42c3241a..b03a0c0a3 100644 --- a/hassio/exceptions.py +++ b/hassio/exceptions.py @@ -149,6 +149,10 @@ class DBusNotConnectedError(HostNotSupportedError): """DBus is not connected and call a method.""" +class DBusInterfaceError(HassioNotSupportedError): + """DBus interface not connected.""" + + class DBusFatalError(DBusError): """DBus call going wrong.""" diff --git a/hassio/hassos.py b/hassio/hassos.py index 13de250cc..b394b71f6 100644 --- a/hassio/hassos.py +++ b/hassio/hassos.py @@ -18,7 +18,7 @@ from .exceptions import ( DockerAPIError, ) -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class HassOS(CoreSysAttributes): diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 8c9919fde..29966e644 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -47,7 +47,7 @@ from .utils import check_port, convert_to_ascii, process_lock from .utils.json import JsonConfig from .validate import SCHEMA_HASS_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml") diff --git a/hassio/host/__init__.py b/hassio/host/__init__.py index bea3fdbb0..50c6bbd41 100644 --- a/hassio/host/__init__.py +++ b/hassio/host/__init__.py @@ -17,7 +17,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from ..exceptions import HassioError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class HostManager(CoreSysAttributes): diff --git a/hassio/host/alsa.py b/hassio/host/alsa.py index e6c736d3e..180fde33c 100644 --- a/hassio/host/alsa.py +++ b/hassio/host/alsa.py @@ -9,7 +9,7 @@ import attr from ..const import ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME, CHAN_ID, CHAN_TYPE from ..coresys import CoreSysAttributes -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=invalid-name DefaultConfig = attr.make_class("DefaultConfig", ["input", "output"]) diff --git a/hassio/host/apparmor.py b/hassio/host/apparmor.py index 6ea68f048..5ce0e86ed 100644 --- a/hassio/host/apparmor.py +++ b/hassio/host/apparmor.py @@ -7,7 +7,7 @@ from ..coresys import CoreSysAttributes from ..exceptions import DBusError, HostAppArmorError from ..utils.apparmor import validate_profile -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) SYSTEMD_SERVICES = {"hassos-apparmor.service", "hassio-apparmor.service"} diff --git a/hassio/host/control.py b/hassio/host/control.py index 8f82dba3d..03cf26bfb 100644 --- a/hassio/host/control.py +++ b/hassio/host/control.py @@ -4,7 +4,7 @@ import logging from ..coresys import CoreSysAttributes from ..exceptions import HostNotSupportedError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) MANAGER = "manager" HOSTNAME = "hostname" diff --git a/hassio/host/info.py b/hassio/host/info.py index cfb4675ae..e3e9187e3 100644 --- a/hassio/host/info.py +++ b/hassio/host/info.py @@ -4,7 +4,7 @@ import logging from ..coresys import CoreSysAttributes from ..exceptions import HassioError, HostNotSupportedError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class InfoCenter(CoreSysAttributes): diff --git a/hassio/host/services.py b/hassio/host/services.py index 0cb409f5f..5eb7c70ba 100644 --- a/hassio/host/services.py +++ b/hassio/host/services.py @@ -6,7 +6,7 @@ import attr from ..coresys import CoreSysAttributes from ..exceptions import HassioError, HostNotSupportedError, HostServiceError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) MOD_REPLACE = "replace" diff --git a/hassio/ingress.py b/hassio/ingress.py index 83dda70b0..f6fad670f 100644 --- a/hassio/ingress.py +++ b/hassio/ingress.py @@ -12,7 +12,7 @@ from .utils.dt import utc_from_timestamp, utcnow from .utils.json import JsonConfig from .validate import SCHEMA_INGRESS_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class Ingress(JsonConfig, CoreSysAttributes): diff --git a/hassio/misc/forwarder.py b/hassio/misc/forwarder.py index 357fd677a..cc30d3d1e 100644 --- a/hassio/misc/forwarder.py +++ b/hassio/misc/forwarder.py @@ -7,7 +7,7 @@ from typing import Optional import async_timeout -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) COMMAND = "socat UDP-RECVFROM:53,fork UDP-SENDTO:{!s}:53" diff --git a/hassio/misc/hardware.py b/hassio/misc/hardware.py index f3038bc1b..512106377 100644 --- a/hassio/misc/hardware.py +++ b/hassio/misc/hardware.py @@ -9,7 +9,7 @@ import pyudev from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) ASOUND_CARDS: Path = Path("/proc/asound/cards") RE_CARDS: re.Pattern = re.compile(r"(\d+) \[(\w*) *\]: (.*\w)") diff --git a/hassio/misc/scheduler.py b/hassio/misc/scheduler.py index 8a7ed139b..3e71fc040 100644 --- a/hassio/misc/scheduler.py +++ b/hassio/misc/scheduler.py @@ -3,7 +3,7 @@ import asyncio from datetime import date, datetime, time, timedelta import logging -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) INTERVAL = "interval" REPEAT = "repeat" diff --git a/hassio/services/modules/mqtt.py b/hassio/services/modules/mqtt.py index 9a85c80fd..882afa9f3 100644 --- a/hassio/services/modules/mqtt.py +++ b/hassio/services/modules/mqtt.py @@ -19,7 +19,7 @@ from ..const import ( ) from ..interface import ServiceInterface -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # pylint: disable=no-value-for-parameter diff --git a/hassio/snapshots/__init__.py b/hassio/snapshots/__init__.py index a426e493a..9595df19e 100644 --- a/hassio/snapshots/__init__.py +++ b/hassio/snapshots/__init__.py @@ -9,7 +9,7 @@ from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL from ..coresys import CoreSysAttributes from ..utils.dt import utcnow -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class SnapshotManager(CoreSysAttributes): diff --git a/hassio/snapshots/snapshot.py b/hassio/snapshots/snapshot.py index 58d35d518..9437d748b 100644 --- a/hassio/snapshots/snapshot.py +++ b/hassio/snapshots/snapshot.py @@ -45,7 +45,7 @@ from ..utils.tar import SecureTarFile from .utils import key_to_iv, password_for_validating, password_to_key, remove_folder from .validate import ALL_FOLDERS, SCHEMA_SNAPSHOT -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class Snapshot(CoreSysAttributes): diff --git a/hassio/store/__init__.py b/hassio/store/__init__.py index 5846348ac..b9bf8cfb1 100644 --- a/hassio/store/__init__.py +++ b/hassio/store/__init__.py @@ -9,7 +9,7 @@ from .addon import AddonStore from .data import StoreData from .repository import Repository -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) BUILTIN_REPOSITORIES = set((REPOSITORY_CORE, REPOSITORY_LOCAL)) diff --git a/hassio/store/addon.py b/hassio/store/addon.py index edf5335dd..3dfcf5cd2 100644 --- a/hassio/store/addon.py +++ b/hassio/store/addon.py @@ -4,7 +4,7 @@ import logging from ..coresys import CoreSys from ..addons.model import AddonModel, Data -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class AddonStore(AddonModel): diff --git a/hassio/store/data.py b/hassio/store/data.py index e3f45bd62..8bb89f343 100644 --- a/hassio/store/data.py +++ b/hassio/store/data.py @@ -20,7 +20,7 @@ from ..utils.json import read_json_file from .utils import extract_hash_from_path from .validate import SCHEMA_REPOSITORY_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class StoreData(CoreSysAttributes): diff --git a/hassio/store/git.py b/hassio/store/git.py index fde95b5f6..7bff4f6ab 100644 --- a/hassio/store/git.py +++ b/hassio/store/git.py @@ -12,7 +12,7 @@ from ..const import URL_HASSIO_ADDONS, ATTR_URL, ATTR_BRANCH from ..coresys import CoreSysAttributes from ..validate import RE_REPOSITORY -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class GitRepo(CoreSysAttributes): diff --git a/hassio/store/utils.py b/hassio/store/utils.py index f0923f99e..91da7880f 100644 --- a/hassio/store/utils.py +++ b/hassio/store/utils.py @@ -5,7 +5,7 @@ from pathlib import Path import re RE_SHA1 = re.compile(r"[a-f0-9]{8}") -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) def get_hash_from_repository(name: str) -> str: diff --git a/hassio/supervisor.py b/hassio/supervisor.py index c18c92d0b..0fc8039ee 100644 --- a/hassio/supervisor.py +++ b/hassio/supervisor.py @@ -20,7 +20,7 @@ from .exceptions import ( SupervisorUpdateError, ) -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class Supervisor(CoreSysAttributes): diff --git a/hassio/tasks.py b/hassio/tasks.py index 3135364b7..fc1dff749 100644 --- a/hassio/tasks.py +++ b/hassio/tasks.py @@ -5,7 +5,7 @@ import logging from .coresys import CoreSysAttributes from .exceptions import HomeAssistantError, CoreDNSError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) HASS_WATCHDOG_API = "HASS_WATCHDOG_API" diff --git a/hassio/updater.py b/hassio/updater.py index e0e18d507..7e9fedd19 100644 --- a/hassio/updater.py +++ b/hassio/updater.py @@ -24,7 +24,7 @@ from .utils import AsyncThrottle from .utils.json import JsonConfig from .validate import SCHEMA_UPDATER_CONFIG -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) class Updater(JsonConfig, CoreSysAttributes): diff --git a/hassio/utils/__init__.py b/hassio/utils/__init__.py index c675c6f6d..4ff19cb18 100644 --- a/hassio/utils/__init__.py +++ b/hassio/utils/__init__.py @@ -5,7 +5,7 @@ import logging import re import socket -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))") diff --git a/hassio/utils/apparmor.py b/hassio/utils/apparmor.py index 838fa1f4f..de92bcdb5 100644 --- a/hassio/utils/apparmor.py +++ b/hassio/utils/apparmor.py @@ -4,7 +4,7 @@ import re from ..exceptions import AppArmorFileError, AppArmorInvalidError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) RE_PROFILE = re.compile(r"^profile ([^ ]+).*$") diff --git a/hassio/utils/dt.py b/hassio/utils/dt.py index afa3f8ea5..972e77527 100644 --- a/hassio/utils/dt.py +++ b/hassio/utils/dt.py @@ -12,7 +12,7 @@ UTC = pytz.utc GEOIP_URL = "http://ip-api.com/json/" -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # Copyright (c) Django Software Foundation and individual contributors. diff --git a/hassio/utils/gdbus.py b/hassio/utils/gdbus.py index ff0f26dee..bd064d75e 100644 --- a/hassio/utils/gdbus.py +++ b/hassio/utils/gdbus.py @@ -1,68 +1,87 @@ """DBus implementation with glib.""" +from __future__ import annotations + import asyncio import logging import json import shlex import re from signal import SIGINT +from typing import Any, Dict, List, Optional, Set import xml.etree.ElementTree as ET -from ..exceptions import DBusFatalError, DBusParseError +from ..exceptions import ( + DBusFatalError, + DBusParseError, + DBusInterfaceError, + DBusNotConnectedError, +) -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) # Use to convert GVariant into json -RE_GVARIANT_TYPE = re.compile( +RE_GVARIANT_TYPE: re.Match = re.compile( r"(?:boolean|byte|int16|uint16|int32|uint32|handle|int64|uint64|double|" r"string|objectpath|signature) " ) -RE_GVARIANT_VARIANT = re.compile( +RE_GVARIANT_VARIANT: re.Match = re.compile( r"(?<=(?: |{|\[))<((?:'|\").*?(?:'|\")|\d+(?:\.\d+)?)>(?=(?:|]|}|,))" ) -RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[|\())'(.*?)'(?=(?:|]|}|,|\)))") -RE_GVARIANT_TUPLE_O = re.compile(r"\"[^\"]*?\"|(\()") -RE_GVARIANT_TUPLE_C = re.compile(r"\"[^\"]*?\"|(,?\))") +RE_GVARIANT_STRING_ESC: re.Match = re.compile( + r"(?<=(?: |{|\[|\())'[^']*?\"[^']*?'(?=(?:|]|}|,|\)))" +) +RE_GVARIANT_STRING: re.Match = re.compile(r"(?<=(?: |{|\[|\())'(.*?)'(?=(?:|]|}|,|\)))") +RE_GVARIANT_TUPLE_O: re.Match = re.compile(r"\"[^\"\\]*(?:\\.[^\"\\]*)*\"|(\()") +RE_GVARIANT_TUPLE_C: re.Match = re.compile(r"\"[^\"\\]*(?:\\.[^\"\\]*)*\"|(,?\))") -RE_MONITOR_OUTPUT = re.compile(r".+?: (?P[^ ].+) (?P.*)") +RE_MONITOR_OUTPUT: re.Match = re.compile(r".+?: (?P[^ ].+) (?P.*)") + +# Map GDBus to errors +MAP_GDBUS_ERROR: Dict[str, Any] = { + "GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown": DBusInterfaceError, + "No such file or directory": DBusNotConnectedError, +} # Commands for dbus -INTROSPECT = "gdbus introspect --system --dest {bus} " "--object-path {object} --xml" -CALL = ( +INTROSPECT: str = "gdbus introspect --system --dest {bus} " "--object-path {object} --xml" +CALL: str = ( "gdbus call --system --dest {bus} --object-path {object} " "--method {method} {args}" ) -MONITOR = "gdbus monitor --system --dest {bus}" +MONITOR: str = "gdbus monitor --system --dest {bus}" -DBUS_METHOD_GETALL = "org.freedesktop.DBus.Properties.GetAll" +DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll" class DBus: """DBus handler.""" - def __init__(self, bus_name, object_path): + def __init__(self, bus_name: str, object_path: str) -> None: """Initialize dbus object.""" - self.bus_name = bus_name - self.object_path = object_path - self.methods = set() - self.signals = set() + self.bus_name: str = bus_name + self.object_path: str = object_path + self.methods: Set[str] = set() + self.signals: Set[str] = set() @staticmethod - async def connect(bus_name, object_path): + async def connect(bus_name: str, object_path: str) -> DBus: """Read object data.""" self = DBus(bus_name, object_path) - await self._init_proxy() # pylint: disable=protected-access + + # pylint: disable=protected-access + await self._init_proxy() _LOGGER.info("Connect to dbus: %s - %s", bus_name, object_path) return self - async def _init_proxy(self): + async def _init_proxy(self) -> None: """Read interface data.""" command = shlex.split( INTROSPECT.format(bus=self.bus_name, object=self.object_path) ) # Ask data - _LOGGER.info("Introspect %s on %s", self.bus_name, self.object_path) + _LOGGER.debug("Introspect %s on %s", self.bus_name, self.object_path) data = await self._send(command) # Parse XML @@ -73,7 +92,7 @@ class DBus: raise DBusParseError() from None # Read available methods - _LOGGER.debug("data: %s", data) + _LOGGER.debug("Introspect XML: %s", data) for interface in xml.findall("./interface"): interface_name = interface.get("name") @@ -88,30 +107,34 @@ class DBus: self.signals.add(f"{interface_name}.{signal_name}") @staticmethod - def parse_gvariant(raw): + def parse_gvariant(raw: str) -> Any: """Parse GVariant input to python.""" - raw = RE_GVARIANT_TYPE.sub("", raw) - raw = RE_GVARIANT_VARIANT.sub(r"\1", raw) - raw = RE_GVARIANT_STRING.sub(r'"\1"', raw) - raw = RE_GVARIANT_TUPLE_O.sub( - lambda x: x.group(0) if not x.group(1) else "[", raw + json_raw: str = RE_GVARIANT_TYPE.sub("", raw) + json_raw = RE_GVARIANT_VARIANT.sub(r"\1", json_raw) + json_raw = RE_GVARIANT_STRING_ESC.sub( + lambda x: x.group(0).replace('"', '\\"'), json_raw ) - raw = RE_GVARIANT_TUPLE_C.sub( - lambda x: x.group(0) if not x.group(1) else "]", raw + json_raw = RE_GVARIANT_STRING.sub(r'"\1"', json_raw) + json_raw = RE_GVARIANT_TUPLE_O.sub( + lambda x: x.group(0) if not x.group(1) else "[", json_raw + ) + json_raw = RE_GVARIANT_TUPLE_C.sub( + lambda x: x.group(0) if not x.group(1) else "]", json_raw ) # No data - if raw.startswith("[]"): + if json_raw.startswith("[]"): return [] try: - return json.loads(raw) + return json.loads(json_raw) except json.JSONDecodeError as err: - _LOGGER.error("Can't parse '%s': %s", raw, err) + _LOGGER.error("Can't parse '%s': %s", json_raw, err) + _LOGGER.debug("GVariant data: '%s'", raw) raise DBusParseError() from None @staticmethod - def gvariant_args(args): + def gvariant_args(args: List[Any]) -> str: """Convert args into gvariant.""" gvariant = "" for arg in args: @@ -122,11 +145,11 @@ class DBus: elif isinstance(arg, str): gvariant += f' "{arg}"' else: - gvariant += " {}".format(str(arg)) + gvariant += f" {arg!s}" return gvariant.lstrip() - async def call_dbus(self, method, *args): + async def call_dbus(self, method: str, *args: List[Any]) -> str: """Call a dbus method.""" command = shlex.split( CALL.format( @@ -142,10 +165,9 @@ class DBus: data = await self._send(command) # Parse and return data - _LOGGER.debug("Receive from %s: %s", method, data) return self.parse_gvariant(data) - async def get_properties(self, interface): + async def get_properties(self, interface: str) -> Dict[str, Any]: """Read all properties from interface.""" try: return (await self.call_dbus(DBUS_METHOD_GETALL, interface))[0] @@ -153,7 +175,7 @@ class DBus: _LOGGER.error("No attributes returned for %s", interface) raise DBusFatalError from None - async def _send(self, command): + async def _send(self, command: List[str]) -> str: """Send command over dbus.""" # Run command _LOGGER.debug("Send dbus command: %s", command) @@ -171,12 +193,19 @@ class DBus: raise DBusFatalError() from None # Success? - if proc.returncode != 0: - _LOGGER.error("DBus return error: %s", error) - raise DBusFatalError() + if proc.returncode == 0: + return data.decode() - # End - return data.decode() + # Filter error + error = error.decode() + for msg, exception in MAP_GDBUS_ERROR.items(): + if msg not in error: + continue + raise exception() + + # General + _LOGGER.error("DBus return error: %s", error) + raise DBusFatalError() def attach_signals(self, filters=None): """Generate a signals wrapper.""" @@ -189,7 +218,7 @@ class DBus: async for signal in signals: return signal - def __getattr__(self, name): + def __getattr__(self, name: str) -> DBusCallWrapper: """Mapping to dbus method.""" return getattr(DBusCallWrapper(self, self.bus_name), name) @@ -197,17 +226,17 @@ class DBus: class DBusCallWrapper: """Wrapper a DBus interface for a call.""" - def __init__(self, dbus, interface): + def __init__(self, dbus: DBus, interface: str) -> None: """Initialize wrapper.""" - self.dbus = dbus - self.interface = interface + self.dbus: DBus = dbus + self.interface: str = interface - def __call__(self): + def __call__(self) -> None: """Should never be called.""" _LOGGER.error("DBus method %s not exists!", self.interface) raise DBusFatalError() - def __getattr__(self, name): + def __getattr__(self, name: str): """Mapping to dbus method.""" interface = f"{self.interface}.{name}" @@ -227,11 +256,11 @@ class DBusCallWrapper: class DBusSignalWrapper: """Process Signals.""" - def __init__(self, dbus, signals=None): + def __init__(self, dbus: DBus, signals: Optional[str] = None): """Initialize dbus signal wrapper.""" - self.dbus = dbus - self._signals = signals - self._proc = None + self.dbus: DBus = dbus + self._signals: Optional[str] = signals + self._proc: Optional[asyncio.Process] = None async def __aenter__(self): """Start monitor events.""" diff --git a/hassio/utils/json.py b/hassio/utils/json.py index fb7e2b8fd..044831b94 100644 --- a/hassio/utils/json.py +++ b/hassio/utils/json.py @@ -9,7 +9,7 @@ from voluptuous.humanize import humanize_error from ..exceptions import JsonFileError -_LOGGER = logging.getLogger(__name__) +_LOGGER: logging.Logger = logging.getLogger(__name__) def write_json_file(jsonfile: Path, data: Any) -> None: diff --git a/tests/utils/test_gvariant_parser.py b/tests/utils/test_gvariant_parser.py new file mode 100644 index 000000000..62f0df60c --- /dev/null +++ b/tests/utils/test_gvariant_parser.py @@ -0,0 +1,278 @@ +"""Test gdbus gvariant parser.""" +from hassio.utils.gdbus import DBus + + +def test_simple_return(): + """Test Simple return value.""" + raw = "(objectpath '/org/freedesktop/systemd1/job/35383',)" + + # parse data + data = DBus.parse_gvariant(raw) + + assert data == ["/org/freedesktop/systemd1/job/35383"] + + +def test_get_property(): + """Test Property parsing.""" + raw = "({'Hostname': <'hassio'>, 'StaticHostname': <'hassio'>, 'PrettyHostname': <''>, 'IconName': <'computer-embedded'>, 'Chassis': <'embedded'>, 'Deployment': <'production'>, 'Location': <''>, 'KernelName': <'Linux'>, 'KernelRelease': <'4.14.98-v7'>, 'KernelVersion': <'#1 SMP Sat May 11 02:17:06 UTC 2019'>, 'OperatingSystemPrettyName': <'HassOS 2.12'>, 'OperatingSystemCPEName': <'cpe:2.3:o:home_assistant:hassos:2.12:*:production:*:*:*:rpi3:*'>, 'HomeURL': <'https://hass.io/'>},)" + + # parse data + data = DBus.parse_gvariant(raw) + + assert data[0] == { + "Hostname": "hassio", + "StaticHostname": "hassio", + "PrettyHostname": "", + "IconName": "computer-embedded", + "Chassis": "embedded", + "Deployment": "production", + "Location": "", + "KernelName": "Linux", + "KernelRelease": "4.14.98-v7", + "KernelVersion": "#1 SMP Sat May 11 02:17:06 UTC 2019", + "OperatingSystemPrettyName": "HassOS 2.12", + "OperatingSystemCPEName": "cpe:2.3:o:home_assistant:hassos:2.12:*:production:*:*:*:rpi3:*", + "HomeURL": "https://hass.io/", + } + + +def test_systemd_unitlist_simple(): + """Test Systemd Unit list simple.""" + raw = "([('systemd-remount-fs.service', 'Remount Root and Kernel File Systems', 'loaded', 'active', 'exited', '', objectpath '/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice', uint32 0, '', objectpath '/'), ('sys-subsystem-net-devices-veth5714b4e.device', '/sys/subsystem/net/devices/veth5714b4e', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice', 0, '', '/'), ('rauc.service', 'Rauc Update Service', 'loaded', 'active', 'running', '', '/org/freedesktop/systemd1/unit/rauc_2eservice', 0, '', '/'), ('mnt-data-docker-overlay2-7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2-merged.mount', '/mnt/data/docker/overlay2/7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2/merged', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/mnt_2ddata_2ddocker_2doverlay2_2d7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2_2dmerged_2emount', 0, '', '/'), ('hassos-hardware.target', 'HassOS hardware targets', 'loaded', 'active', 'active', '', '/org/freedesktop/systemd1/unit/hassos_2dhardware_2etarget', 0, '', '/'), ('dev-zram1.device', '/dev/zram1', 'loaded', 'active', 'plugged', 'sys-devices-virtual-block-zram1.device', '/org/freedesktop/systemd1/unit/dev_2dzram1_2edevice', 0, '', '/'), ('sys-subsystem-net-devices-hassio.device', '/sys/subsystem/net/devices/hassio', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dhassio_2edevice', 0, '', '/'), ('cryptsetup.target', 'cryptsetup.target', 'not-found', 'inactive', 'dead', '', '/org/freedesktop/systemd1/unit/cryptsetup_2etarget', 0, '', '/'), ('sys-devices-virtual-net-vethd256dfa.device', '/sys/devices/virtual/net/vethd256dfa', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dvethd256dfa_2edevice', 0, '', '/'), ('network-pre.target', 'Network (Pre)', 'loaded', 'inactive', 'dead', '', '/org/freedesktop/systemd1/unit/network_2dpre_2etarget', 0, '', '/'), ('sys-devices-virtual-net-veth5714b4e.device', '/sys/devices/virtual/net/veth5714b4e', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dveth5714b4e_2edevice', 0, '', '/'), ('sys-kernel-debug.mount', 'Kernel Debug File System', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/sys_2dkernel_2ddebug_2emount', 0, '', '/'), ('slices.target', 'Slices', 'loaded', 'active', 'active', '', '/org/freedesktop/systemd1/unit/slices_2etarget', 0, '', '/'), ('etc-NetworkManager-system\x2dconnections.mount', 'NetworkManager persistent system connections', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/etc_2dNetworkManager_2dsystem_5cx2dconnections_2emount', 0, '', '/'), ('run-docker-netns-26ede3178729.mount', '/run/docker/netns/26ede3178729', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/run_2ddocker_2dnetns_2d26ede3178729_2emount', 0, '', '/'), ('dev-disk-by\x2dpath-platform\x2d3f202000.mmc\x2dpart2.device', '/dev/disk/by-path/platform-3f202000.mmc-part2', 'loaded', 'active', 'plugged', 'sys-devices-platform-soc-3f202000.mmc-mmc_host-mmc0-mmc0:e624-block-mmcblk0-mmcblk0p2.device', '/org/freedesktop/systemd1/unit/dev_2ddisk_2dby_5cx2dpath_2dplatform_5cx2d3f202000_2emmc_5cx2dpart2_2edevice', 0, '', '/')],)" + + # parse data + data = DBus.parse_gvariant(raw) + + assert data == [ + [ + [ + "systemd-remount-fs.service", + "Remount Root and Kernel File Systems", + "loaded", + "active", + "exited", + "", + "/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice", + 0, + "", + "/", + ], + [ + "sys-subsystem-net-devices-veth5714b4e.device", + "/sys/subsystem/net/devices/veth5714b4e", + "loaded", + "active", + "plugged", + "", + "/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice", + 0, + "", + "/", + ], + [ + "rauc.service", + "Rauc Update Service", + "loaded", + "active", + "running", + "", + "/org/freedesktop/systemd1/unit/rauc_2eservice", + 0, + "", + "/", + ], + [ + "mnt-data-docker-overlay2-7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2-merged.mount", + "/mnt/data/docker/overlay2/7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2/merged", + "loaded", + "active", + "mounted", + "", + "/org/freedesktop/systemd1/unit/mnt_2ddata_2ddocker_2doverlay2_2d7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2_2dmerged_2emount", + 0, + "", + "/", + ], + [ + "hassos-hardware.target", + "HassOS hardware targets", + "loaded", + "active", + "active", + "", + "/org/freedesktop/systemd1/unit/hassos_2dhardware_2etarget", + 0, + "", + "/", + ], + [ + "dev-zram1.device", + "/dev/zram1", + "loaded", + "active", + "plugged", + "sys-devices-virtual-block-zram1.device", + "/org/freedesktop/systemd1/unit/dev_2dzram1_2edevice", + 0, + "", + "/", + ], + [ + "sys-subsystem-net-devices-hassio.device", + "/sys/subsystem/net/devices/hassio", + "loaded", + "active", + "plugged", + "", + "/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dhassio_2edevice", + 0, + "", + "/", + ], + [ + "cryptsetup.target", + "cryptsetup.target", + "not-found", + "inactive", + "dead", + "", + "/org/freedesktop/systemd1/unit/cryptsetup_2etarget", + 0, + "", + "/", + ], + [ + "sys-devices-virtual-net-vethd256dfa.device", + "/sys/devices/virtual/net/vethd256dfa", + "loaded", + "active", + "plugged", + "", + "/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dvethd256dfa_2edevice", + 0, + "", + "/", + ], + [ + "network-pre.target", + "Network (Pre)", + "loaded", + "inactive", + "dead", + "", + "/org/freedesktop/systemd1/unit/network_2dpre_2etarget", + 0, + "", + "/", + ], + [ + "sys-devices-virtual-net-veth5714b4e.device", + "/sys/devices/virtual/net/veth5714b4e", + "loaded", + "active", + "plugged", + "", + "/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dveth5714b4e_2edevice", + 0, + "", + "/", + ], + [ + "sys-kernel-debug.mount", + "Kernel Debug File System", + "loaded", + "active", + "mounted", + "", + "/org/freedesktop/systemd1/unit/sys_2dkernel_2ddebug_2emount", + 0, + "", + "/", + ], + [ + "slices.target", + "Slices", + "loaded", + "active", + "active", + "", + "/org/freedesktop/systemd1/unit/slices_2etarget", + 0, + "", + "/", + ], + [ + "etc-NetworkManager-system-connections.mount", + "NetworkManager persistent system connections", + "loaded", + "active", + "mounted", + "", + "/org/freedesktop/systemd1/unit/etc_2dNetworkManager_2dsystem_5cx2dconnections_2emount", + 0, + "", + "/", + ], + [ + "run-docker-netns-26ede3178729.mount", + "/run/docker/netns/26ede3178729", + "loaded", + "active", + "mounted", + "", + "/org/freedesktop/systemd1/unit/run_2ddocker_2dnetns_2d26ede3178729_2emount", + 0, + "", + "/", + ], + [ + "dev-disk-by-path-platform-3f202000.mmc-part2.device", + "/dev/disk/by-path/platform-3f202000.mmc-part2", + "loaded", + "active", + "plugged", + "sys-devices-platform-soc-3f202000.mmc-mmc_host-mmc0-mmc0:e624-block-mmcblk0-mmcblk0p2.device", + "/org/freedesktop/systemd1/unit/dev_2ddisk_2dby_5cx2dpath_2dplatform_5cx2d3f202000_2emmc_5cx2dpart2_2edevice", + 0, + "", + "/", + ], + ] + ] + + +def test_systemd_unitlist_complex(): + """Test Systemd Unit list simple.""" + raw = "([('systemd-remount-fs.service', 'Remount Root and \"Kernel File Systems\"', 'loaded', 'active', 'exited', '', objectpath '/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice', uint32 0, '', objectpath '/'), ('sys-subsystem-net-devices-veth5714b4e.device', '/sys/subsystem/net/devices/veth5714b4e for \" is', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice', 0, '', '/')],)" + + # parse data + data = DBus.parse_gvariant(raw) + + assert data == [ + [ + [ + "systemd-remount-fs.service", + 'Remount Root and "Kernel File Systems"', + "loaded", + "active", + "exited", + "", + "/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice", + 0, + "", + "/", + ], + [ + "sys-subsystem-net-devices-veth5714b4e.device", + '/sys/subsystem/net/devices/veth5714b4e for " is', + "loaded", + "active", + "plugged", + "", + "/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice", + 0, + "", + "/", + ], + ] + ]