From 02c95418622ffe5a221fa5e75a56d16f102f4efd Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 13 Sep 2022 21:55:50 +0200 Subject: [PATCH] Improve type hints in mailbox (#78353) --- .../components/asterisk_mbox/mailbox.py | 28 +++++----- homeassistant/components/mailbox/__init__.py | 51 ++++++++++--------- pylint/plugins/hass_enforce_type_hints.py | 33 ++++++++++++ 3 files changed, 76 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/asterisk_mbox/mailbox.py b/homeassistant/components/asterisk_mbox/mailbox.py index 04d2be70704..edf95cb3787 100644 --- a/homeassistant/components/asterisk_mbox/mailbox.py +++ b/homeassistant/components/asterisk_mbox/mailbox.py @@ -3,6 +3,7 @@ from __future__ import annotations from functools import partial import logging +from typing import Any from asterisk_mbox import ServerError @@ -11,7 +12,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import DOMAIN as ASTERISK_DOMAIN +from . import DOMAIN as ASTERISK_DOMAIN, AsteriskData _LOGGER = logging.getLogger(__name__) @@ -31,7 +32,7 @@ async def async_get_handler( class AsteriskMailbox(Mailbox): """Asterisk VM Sensor.""" - def __init__(self, hass, name): + def __init__(self, hass: HomeAssistant, name: str) -> None: """Initialize Asterisk mailbox.""" super().__init__(hass, name) async_dispatcher_connect( @@ -39,29 +40,30 @@ class AsteriskMailbox(Mailbox): ) @callback - def _update_callback(self, msg): + def _update_callback(self, msg: str) -> None: """Update the message count in HA, if needed.""" self.async_update() @property - def media_type(self): + def media_type(self) -> str: """Return the supported media type.""" return CONTENT_TYPE_MPEG @property - def can_delete(self): + def can_delete(self) -> bool: """Return if messages can be deleted.""" return True @property - def has_media(self): + def has_media(self) -> bool: """Return if messages have attached media files.""" return True - async def async_get_media(self, msgid): + async def async_get_media(self, msgid: str) -> bytes: """Return the media blob for the msgid.""" - client = self.hass.data[ASTERISK_DOMAIN].client + data: AsteriskData = self.hass.data[ASTERISK_DOMAIN] + client = data.client try: return await self.hass.async_add_executor_job( partial(client.mp3, msgid, sync=True) @@ -69,13 +71,15 @@ class AsteriskMailbox(Mailbox): except ServerError as err: raise StreamError(err) from err - async def async_get_messages(self): + async def async_get_messages(self) -> list[dict[str, Any]]: """Return a list of the current messages.""" - return self.hass.data[ASTERISK_DOMAIN].messages + data: AsteriskData = self.hass.data[ASTERISK_DOMAIN] + return data.messages - async def async_delete(self, msgid): + async def async_delete(self, msgid: str) -> bool: """Delete the specified messages.""" - client = self.hass.data[ASTERISK_DOMAIN].client + data: AsteriskData = self.hass.data[ASTERISK_DOMAIN] + client = data.client _LOGGER.info("Deleting: %s", msgid) await self.hass.async_add_executor_job(client.delete, msgid) return True diff --git a/homeassistant/components/mailbox/__init__.py b/homeassistant/components/mailbox/__init__.py index b6a727083c9..4e65d989b98 100644 --- a/homeassistant/components/mailbox/__init__.py +++ b/homeassistant/components/mailbox/__init__.py @@ -6,6 +6,7 @@ from contextlib import suppress from datetime import timedelta from http import HTTPStatus import logging +from typing import Any, Final from aiohttp import web from aiohttp.web_exceptions import HTTPNotFound @@ -13,7 +14,7 @@ import async_timeout from homeassistant.components import frontend from homeassistant.components.http import HomeAssistantView -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, discovery from homeassistant.helpers.entity import Entity @@ -21,15 +22,13 @@ from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.setup import async_prepare_setup_platform -# mypy: allow-untyped-defs, no-check-untyped-defs - _LOGGER = logging.getLogger(__name__) -DOMAIN = "mailbox" +DOMAIN: Final = "mailbox" -EVENT = "mailbox_updated" -CONTENT_TYPE_MPEG = "audio/mpeg" -CONTENT_TYPE_NONE = "none" +EVENT: Final = "mailbox_updated" +CONTENT_TYPE_MPEG: Final = "audio/mpeg" +CONTENT_TYPE_NONE: Final = "none" SCAN_INTERVAL = timedelta(seconds=30) @@ -98,7 +97,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if setup_tasks: await asyncio.wait(setup_tasks) - async def async_platform_discovered(platform, info): + async def async_platform_discovered( + platform: str, info: DiscoveryInfoType | None + ) -> None: """Handle for discovered platform.""" await async_setup_platform(platform, discovery_info=info) @@ -115,27 +116,27 @@ class MailboxEntity(Entity): self.mailbox = mailbox self.message_count = 0 - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """Complete entity initialization.""" @callback - def _mailbox_updated(event): + def _mailbox_updated(event: Event) -> None: self.async_schedule_update_ha_state(True) self.hass.bus.async_listen(EVENT, _mailbox_updated) self.async_schedule_update_ha_state(True) @property - def state(self): + def state(self) -> str: """Return the state of the binary sensor.""" return str(self.message_count) @property - def name(self): + def name(self) -> str: """Return the name of the entity.""" return self.mailbox.name - async def async_update(self): + async def async_update(self) -> None: """Retrieve messages from platform.""" messages = await self.mailbox.async_get_messages() self.message_count = len(messages) @@ -155,29 +156,29 @@ class Mailbox: self.hass.bus.async_fire(EVENT) @property - def media_type(self): + def media_type(self) -> str: """Return the supported media type.""" raise NotImplementedError() @property - def can_delete(self): + def can_delete(self) -> bool: """Return if messages can be deleted.""" return False @property - def has_media(self): + def has_media(self) -> bool: """Return if messages have attached media files.""" return False - async def async_get_media(self, msgid): + async def async_get_media(self, msgid: str) -> bytes: """Return the media blob for the msgid.""" raise NotImplementedError() - async def async_get_messages(self): + async def async_get_messages(self) -> list[dict[str, Any]]: """Return a list of the current messages.""" raise NotImplementedError() - async def async_delete(self, msgid): + async def async_delete(self, msgid: str) -> bool: """Delete the specified messages.""" raise NotImplementedError() @@ -193,7 +194,7 @@ class MailboxView(HomeAssistantView): """Initialize a basic mailbox view.""" self.mailboxes = mailboxes - def get_mailbox(self, platform): + def get_mailbox(self, platform: str) -> Mailbox: """Retrieve the specified mailbox.""" for mailbox in self.mailboxes: if mailbox.name == platform: @@ -209,7 +210,7 @@ class MailboxPlatformsView(MailboxView): async def get(self, request: web.Request) -> web.Response: """Retrieve list of platforms.""" - platforms = [] + platforms: list[dict[str, Any]] = [] for mailbox in self.mailboxes: platforms.append( { @@ -227,7 +228,7 @@ class MailboxMessageView(MailboxView): url = "/api/mailbox/messages/{platform}" name = "api:mailbox:messages" - async def get(self, request, platform): + async def get(self, request: web.Request, platform: str) -> web.Response: """Retrieve messages.""" mailbox = self.get_mailbox(platform) messages = await mailbox.async_get_messages() @@ -240,7 +241,7 @@ class MailboxDeleteView(MailboxView): url = "/api/mailbox/delete/{platform}/{msgid}" name = "api:mailbox:delete" - async def delete(self, request, platform, msgid): + async def delete(self, request: web.Request, platform: str, msgid: str) -> None: """Delete items.""" mailbox = self.get_mailbox(platform) await mailbox.async_delete(msgid) @@ -252,7 +253,9 @@ class MailboxMediaView(MailboxView): url = r"/api/mailbox/media/{platform}/{msgid}" name = "api:asteriskmbox:media" - async def get(self, request, platform, msgid): + async def get( + self, request: web.Request, platform: str, msgid: str + ) -> web.Response: """Retrieve media.""" mailbox = self.get_mailbox(platform) diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index cb0c6ecc2e7..7cd94b3181c 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -1539,6 +1539,39 @@ _INHERITANCE_MATCH: dict[str, list[ClassTypeHintMatch]] = { ], ), ], + "mailbox": [ + ClassTypeHintMatch( + base_class="Mailbox", + matches=[ + TypeHintMatch( + function_name="media_type", + return_type="str", + ), + TypeHintMatch( + function_name="can_delete", + return_type="bool", + ), + TypeHintMatch( + function_name="has_media", + return_type="bool", + ), + TypeHintMatch( + function_name="async_get_media", + arg_types={1: "str"}, + return_type="bytes", + ), + TypeHintMatch( + function_name="async_get_messages", + return_type="list[dict[str, Any]]", + ), + TypeHintMatch( + function_name="async_delete", + arg_types={1: "str"}, + return_type="bool", + ), + ], + ), + ], "media_player": [ ClassTypeHintMatch( base_class="Entity",