Enable disallow-any-generics in media-player (#78498)

Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
This commit is contained in:
epenet 2022-09-17 21:23:16 +02:00 committed by GitHub
parent b51fd7f688
commit 391d895426
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -12,7 +12,7 @@ import hashlib
from http import HTTPStatus from http import HTTPStatus
import logging import logging
import secrets import secrets
from typing import Any, cast, final from typing import Any, Final, TypedDict, final
from urllib.parse import quote, urlparse from urllib.parse import quote, urlparse
from aiohttp import web from aiohttp import web
@ -136,12 +136,11 @@ _LOGGER = logging.getLogger(__name__)
ENTITY_ID_FORMAT = DOMAIN + ".{}" ENTITY_ID_FORMAT = DOMAIN + ".{}"
CACHE_IMAGES = "images" CACHE_IMAGES: Final = "images"
CACHE_MAXSIZE = "maxsize" CACHE_MAXSIZE: Final = "maxsize"
CACHE_LOCK = "lock" CACHE_LOCK: Final = "lock"
CACHE_URL = "url" CACHE_URL: Final = "url"
CACHE_CONTENT = "content" CACHE_CONTENT: Final = "content"
ENTITY_IMAGE_CACHE = {CACHE_IMAGES: collections.OrderedDict(), CACHE_MAXSIZE: 16}
SCAN_INTERVAL = dt.timedelta(seconds=10) SCAN_INTERVAL = dt.timedelta(seconds=10)
@ -214,6 +213,25 @@ ATTR_TO_PROPERTY = [
ATTR_MEDIA_REPEAT, ATTR_MEDIA_REPEAT,
] ]
# mypy: disallow-any-generics
class _CacheImage(TypedDict, total=False):
"""Class to hold a cached image."""
lock: asyncio.Lock
content: tuple[bytes | None, str | None]
class _ImageCache(TypedDict):
"""Class to hold a cached image."""
images: collections.OrderedDict[str, _CacheImage]
maxsize: int
_ENTITY_IMAGE_CACHE = _ImageCache(images=collections.OrderedDict(), maxsize=16)
@bind_hass @bind_hass
def is_on(hass: HomeAssistant, entity_id: str | None = None) -> bool: def is_on(hass: HomeAssistant, entity_id: str | None = None) -> bool:
@ -248,7 +266,7 @@ def _rename_keys(**keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Track states and offer events for media_players.""" """Track states and offer events for media_players."""
component = hass.data[DOMAIN] = EntityComponent( component = hass.data[DOMAIN] = EntityComponent[MediaPlayerEntity](
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL
) )
@ -420,13 +438,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a config entry.""" """Set up a config entry."""
component: EntityComponent = hass.data[DOMAIN] component: EntityComponent[MediaPlayerEntity] = hass.data[DOMAIN]
return await component.async_setup_entry(entry) return await component.async_setup_entry(entry)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
component: EntityComponent = hass.data[DOMAIN] component: EntityComponent[MediaPlayerEntity] = hass.data[DOMAIN]
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@ -1060,8 +1078,8 @@ class MediaPlayerEntity(Entity):
Images are cached in memory (the images are typically 10-100kB in size). Images are cached in memory (the images are typically 10-100kB in size).
""" """
cache_images = cast(collections.OrderedDict, ENTITY_IMAGE_CACHE[CACHE_IMAGES]) cache_images = _ENTITY_IMAGE_CACHE[CACHE_IMAGES]
cache_maxsize = cast(int, ENTITY_IMAGE_CACHE[CACHE_MAXSIZE]) cache_maxsize = _ENTITY_IMAGE_CACHE[CACHE_MAXSIZE]
if urlparse(url).hostname is None: if urlparse(url).hostname is None:
url = f"{get_url(self.hass)}{url}" url = f"{get_url(self.hass)}{url}"
@ -1071,7 +1089,7 @@ class MediaPlayerEntity(Entity):
async with cache_images[url][CACHE_LOCK]: async with cache_images[url][CACHE_LOCK]:
if CACHE_CONTENT in cache_images[url]: if CACHE_CONTENT in cache_images[url]:
return cache_images[url][CACHE_CONTENT] # type:ignore[no-any-return] return cache_images[url][CACHE_CONTENT]
(content, content_type) = await self._async_fetch_image(url) (content, content_type) = await self._async_fetch_image(url)
@ -1120,7 +1138,7 @@ class MediaPlayerImageView(HomeAssistantView):
+ "/browse_media/{media_content_type}/{media_content_id:.+}", + "/browse_media/{media_content_type}/{media_content_id:.+}",
] ]
def __init__(self, component: EntityComponent) -> None: def __init__(self, component: EntityComponent[MediaPlayerEntity]) -> None:
"""Initialize a media player view.""" """Initialize a media player view."""
self.component = component self.component = component
@ -1191,8 +1209,8 @@ async def websocket_browse_media(
To use, media_player integrations can implement MediaPlayerEntity.async_browse_media() To use, media_player integrations can implement MediaPlayerEntity.async_browse_media()
""" """
component = hass.data[DOMAIN] component: EntityComponent[MediaPlayerEntity] = hass.data[DOMAIN]
player: MediaPlayerEntity | None = component.get_entity(msg["entity_id"]) player = component.get_entity(msg["entity_id"])
if player is None: if player is None:
connection.send_error(msg["id"], "entity_not_found", "Entity not found") connection.send_error(msg["id"], "entity_not_found", "Entity not found")