Improve type hints in mailbox (#78353)

This commit is contained in:
epenet 2022-09-13 21:55:50 +02:00 committed by GitHub
parent 49ab5cfc9c
commit 02c9541862
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 36 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from functools import partial from functools import partial
import logging import logging
from typing import Any
from asterisk_mbox import ServerError 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.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN as ASTERISK_DOMAIN from . import DOMAIN as ASTERISK_DOMAIN, AsteriskData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -31,7 +32,7 @@ async def async_get_handler(
class AsteriskMailbox(Mailbox): class AsteriskMailbox(Mailbox):
"""Asterisk VM Sensor.""" """Asterisk VM Sensor."""
def __init__(self, hass, name): def __init__(self, hass: HomeAssistant, name: str) -> None:
"""Initialize Asterisk mailbox.""" """Initialize Asterisk mailbox."""
super().__init__(hass, name) super().__init__(hass, name)
async_dispatcher_connect( async_dispatcher_connect(
@ -39,29 +40,30 @@ class AsteriskMailbox(Mailbox):
) )
@callback @callback
def _update_callback(self, msg): def _update_callback(self, msg: str) -> None:
"""Update the message count in HA, if needed.""" """Update the message count in HA, if needed."""
self.async_update() self.async_update()
@property @property
def media_type(self): def media_type(self) -> str:
"""Return the supported media type.""" """Return the supported media type."""
return CONTENT_TYPE_MPEG return CONTENT_TYPE_MPEG
@property @property
def can_delete(self): def can_delete(self) -> bool:
"""Return if messages can be deleted.""" """Return if messages can be deleted."""
return True return True
@property @property
def has_media(self): def has_media(self) -> bool:
"""Return if messages have attached media files.""" """Return if messages have attached media files."""
return True 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.""" """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: try:
return await self.hass.async_add_executor_job( return await self.hass.async_add_executor_job(
partial(client.mp3, msgid, sync=True) partial(client.mp3, msgid, sync=True)
@ -69,13 +71,15 @@ class AsteriskMailbox(Mailbox):
except ServerError as err: except ServerError as err:
raise StreamError(err) from 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 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.""" """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) _LOGGER.info("Deleting: %s", msgid)
await self.hass.async_add_executor_job(client.delete, msgid) await self.hass.async_add_executor_job(client.delete, msgid)
return True return True

View File

@ -6,6 +6,7 @@ from contextlib import suppress
from datetime import timedelta from datetime import timedelta
from http import HTTPStatus from http import HTTPStatus
import logging import logging
from typing import Any, Final
from aiohttp import web from aiohttp import web
from aiohttp.web_exceptions import HTTPNotFound from aiohttp.web_exceptions import HTTPNotFound
@ -13,7 +14,7 @@ import async_timeout
from homeassistant.components import frontend from homeassistant.components import frontend
from homeassistant.components.http import HomeAssistantView 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.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform, discovery from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.entity import Entity 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.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.setup import async_prepare_setup_platform from homeassistant.setup import async_prepare_setup_platform
# mypy: allow-untyped-defs, no-check-untyped-defs
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = "mailbox" DOMAIN: Final = "mailbox"
EVENT = "mailbox_updated" EVENT: Final = "mailbox_updated"
CONTENT_TYPE_MPEG = "audio/mpeg" CONTENT_TYPE_MPEG: Final = "audio/mpeg"
CONTENT_TYPE_NONE = "none" CONTENT_TYPE_NONE: Final = "none"
SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = timedelta(seconds=30)
@ -98,7 +97,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if setup_tasks: if setup_tasks:
await asyncio.wait(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.""" """Handle for discovered platform."""
await async_setup_platform(platform, discovery_info=info) await async_setup_platform(platform, discovery_info=info)
@ -115,27 +116,27 @@ class MailboxEntity(Entity):
self.mailbox = mailbox self.mailbox = mailbox
self.message_count = 0 self.message_count = 0
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Complete entity initialization.""" """Complete entity initialization."""
@callback @callback
def _mailbox_updated(event): def _mailbox_updated(event: Event) -> None:
self.async_schedule_update_ha_state(True) self.async_schedule_update_ha_state(True)
self.hass.bus.async_listen(EVENT, _mailbox_updated) self.hass.bus.async_listen(EVENT, _mailbox_updated)
self.async_schedule_update_ha_state(True) self.async_schedule_update_ha_state(True)
@property @property
def state(self): def state(self) -> str:
"""Return the state of the binary sensor.""" """Return the state of the binary sensor."""
return str(self.message_count) return str(self.message_count)
@property @property
def name(self): def name(self) -> str:
"""Return the name of the entity.""" """Return the name of the entity."""
return self.mailbox.name return self.mailbox.name
async def async_update(self): async def async_update(self) -> None:
"""Retrieve messages from platform.""" """Retrieve messages from platform."""
messages = await self.mailbox.async_get_messages() messages = await self.mailbox.async_get_messages()
self.message_count = len(messages) self.message_count = len(messages)
@ -155,29 +156,29 @@ class Mailbox:
self.hass.bus.async_fire(EVENT) self.hass.bus.async_fire(EVENT)
@property @property
def media_type(self): def media_type(self) -> str:
"""Return the supported media type.""" """Return the supported media type."""
raise NotImplementedError() raise NotImplementedError()
@property @property
def can_delete(self): def can_delete(self) -> bool:
"""Return if messages can be deleted.""" """Return if messages can be deleted."""
return False return False
@property @property
def has_media(self): def has_media(self) -> bool:
"""Return if messages have attached media files.""" """Return if messages have attached media files."""
return False 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.""" """Return the media blob for the msgid."""
raise NotImplementedError() 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.""" """Return a list of the current messages."""
raise NotImplementedError() raise NotImplementedError()
async def async_delete(self, msgid): async def async_delete(self, msgid: str) -> bool:
"""Delete the specified messages.""" """Delete the specified messages."""
raise NotImplementedError() raise NotImplementedError()
@ -193,7 +194,7 @@ class MailboxView(HomeAssistantView):
"""Initialize a basic mailbox view.""" """Initialize a basic mailbox view."""
self.mailboxes = mailboxes self.mailboxes = mailboxes
def get_mailbox(self, platform): def get_mailbox(self, platform: str) -> Mailbox:
"""Retrieve the specified mailbox.""" """Retrieve the specified mailbox."""
for mailbox in self.mailboxes: for mailbox in self.mailboxes:
if mailbox.name == platform: if mailbox.name == platform:
@ -209,7 +210,7 @@ class MailboxPlatformsView(MailboxView):
async def get(self, request: web.Request) -> web.Response: async def get(self, request: web.Request) -> web.Response:
"""Retrieve list of platforms.""" """Retrieve list of platforms."""
platforms = [] platforms: list[dict[str, Any]] = []
for mailbox in self.mailboxes: for mailbox in self.mailboxes:
platforms.append( platforms.append(
{ {
@ -227,7 +228,7 @@ class MailboxMessageView(MailboxView):
url = "/api/mailbox/messages/{platform}" url = "/api/mailbox/messages/{platform}"
name = "api:mailbox:messages" name = "api:mailbox:messages"
async def get(self, request, platform): async def get(self, request: web.Request, platform: str) -> web.Response:
"""Retrieve messages.""" """Retrieve messages."""
mailbox = self.get_mailbox(platform) mailbox = self.get_mailbox(platform)
messages = await mailbox.async_get_messages() messages = await mailbox.async_get_messages()
@ -240,7 +241,7 @@ class MailboxDeleteView(MailboxView):
url = "/api/mailbox/delete/{platform}/{msgid}" url = "/api/mailbox/delete/{platform}/{msgid}"
name = "api:mailbox:delete" 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.""" """Delete items."""
mailbox = self.get_mailbox(platform) mailbox = self.get_mailbox(platform)
await mailbox.async_delete(msgid) await mailbox.async_delete(msgid)
@ -252,7 +253,9 @@ class MailboxMediaView(MailboxView):
url = r"/api/mailbox/media/{platform}/{msgid}" url = r"/api/mailbox/media/{platform}/{msgid}"
name = "api:asteriskmbox:media" 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.""" """Retrieve media."""
mailbox = self.get_mailbox(platform) mailbox = self.get_mailbox(platform)

View File

@ -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": [ "media_player": [
ClassTypeHintMatch( ClassTypeHintMatch(
base_class="Entity", base_class="Entity",