Add strict typing for lidarr (#79241)

This commit is contained in:
Marc Mueller 2022-10-07 20:54:29 +02:00 committed by GitHub
parent a809f645a7
commit 14d2bbfcd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 37 deletions

View File

@ -162,6 +162,7 @@ homeassistant.components.lacrosse_view.*
homeassistant.components.lametric.*
homeassistant.components.laundrify.*
homeassistant.components.lcn.*
homeassistant.components.lidarr.*
homeassistant.components.lifx.*
homeassistant.components.light.*
homeassistant.components.litterrobot.*

View File

@ -1,6 +1,8 @@
"""The Lidarr component."""
from __future__ import annotations
from typing import Any
from aiopyarr.lidarr_client import LidarrClient
from aiopyarr.models.host_configuration import PyArrHostConfiguration
@ -18,6 +20,7 @@ from .coordinator import (
LidarrDataUpdateCoordinator,
QueueDataUpdateCoordinator,
StatusDataUpdateCoordinator,
T,
WantedDataUpdateCoordinator,
)
@ -36,7 +39,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
session=async_get_clientsession(hass, host_configuration.verify_ssl),
request_timeout=60,
)
coordinators: dict[str, LidarrDataUpdateCoordinator] = {
coordinators: dict[str, LidarrDataUpdateCoordinator[Any]] = {
"disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, lidarr),
"queue": QueueDataUpdateCoordinator(hass, host_configuration, lidarr),
"status": StatusDataUpdateCoordinator(hass, host_configuration, lidarr),
@ -63,13 +66,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class LidarrEntity(CoordinatorEntity[LidarrDataUpdateCoordinator]):
class LidarrEntity(CoordinatorEntity[LidarrDataUpdateCoordinator[T]]):
"""Defines a base Lidarr entity."""
_attr_has_entity_name = True
def __init__(
self, coordinator: LidarrDataUpdateCoordinator, description: EntityDescription
self,
coordinator: LidarrDataUpdateCoordinator[T],
description: EntityDescription,
) -> None:
"""Initialize the Lidarr entity."""
super().__init__(coordinator)

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from abc import abstractmethod
from datetime import timedelta
from typing import Generic, TypeVar, cast
from typing import Generic, TypeVar, Union, cast
from aiopyarr import LidarrAlbum, LidarrQueue, LidarrRootFolder, exceptions
from aiopyarr.lidarr_client import LidarrClient
@ -16,10 +16,10 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DEFAULT_MAX_RECORDS, DOMAIN, LOGGER
T = TypeVar("T", list[LidarrRootFolder], LidarrQueue, str, LidarrAlbum)
T = TypeVar("T", bound=Union[list[LidarrRootFolder], LidarrQueue, str, LidarrAlbum])
class LidarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]):
class LidarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T]):
"""Data update coordinator for the Lidarr integration."""
config_entry: ConfigEntry
@ -59,15 +59,19 @@ class LidarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]):
raise NotImplementedError
class DiskSpaceDataUpdateCoordinator(LidarrDataUpdateCoordinator):
class DiskSpaceDataUpdateCoordinator(
LidarrDataUpdateCoordinator[list[LidarrRootFolder]]
):
"""Disk space update coordinator for Lidarr."""
async def _fetch_data(self) -> list[LidarrRootFolder]:
"""Fetch the data."""
return cast(list, await self.api_client.async_get_root_folders())
return cast(
list[LidarrRootFolder], await self.api_client.async_get_root_folders()
)
class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator):
class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator[LidarrQueue]):
"""Queue update coordinator."""
async def _fetch_data(self) -> LidarrQueue:
@ -75,7 +79,7 @@ class QueueDataUpdateCoordinator(LidarrDataUpdateCoordinator):
return await self.api_client.async_get_queue(page_size=DEFAULT_MAX_RECORDS)
class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator):
class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator[str]):
"""Status update coordinator for Lidarr."""
async def _fetch_data(self) -> str:
@ -83,7 +87,7 @@ class StatusDataUpdateCoordinator(LidarrDataUpdateCoordinator):
return (await self.api_client.async_get_system_status()).version
class WantedDataUpdateCoordinator(LidarrDataUpdateCoordinator):
class WantedDataUpdateCoordinator(LidarrDataUpdateCoordinator[LidarrAlbum]):
"""Wanted update coordinator."""
async def _fetch_data(self) -> LidarrAlbum:

View File

@ -4,10 +4,9 @@ from __future__ import annotations
from collections.abc import Callable
from copy import deepcopy
from dataclasses import dataclass
from datetime import datetime
from typing import Generic
from typing import Any, Generic
from aiopyarr import LidarrQueueItem, LidarrRootFolder
from aiopyarr import LidarrQueue, LidarrQueueItem, LidarrRootFolder
from homeassistant.components.sensor import (
SensorEntity,
@ -18,7 +17,6 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import DATA_GIGABYTES
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from . import LidarrEntity
from .const import BYTE_SIZES, DOMAIN
@ -27,7 +25,7 @@ from .coordinator import LidarrDataUpdateCoordinator, T
def get_space(data: list[LidarrRootFolder], name: str) -> str:
"""Get space."""
space = []
space: list[float] = []
for mount in data:
if name in mount.path:
mount.freeSpace = mount.freeSpace if mount.accessible else 0
@ -36,8 +34,8 @@ def get_space(data: list[LidarrRootFolder], name: str) -> str:
def get_modified_description(
description: LidarrSensorEntityDescription, mount: LidarrRootFolder
) -> tuple[LidarrSensorEntityDescription, str]:
description: LidarrSensorEntityDescription[T], mount: LidarrRootFolder
) -> tuple[LidarrSensorEntityDescription[T], str]:
"""Return modified description and folder name."""
desc = deepcopy(description)
name = mount.path.rsplit("/")[-1].rsplit("\\")[-1]
@ -50,25 +48,23 @@ def get_modified_description(
class LidarrSensorEntityDescriptionMixIn(Generic[T]):
"""Mixin for required keys."""
value_fn: Callable[[T, str], str]
value_fn: Callable[[T, str], str | int]
@dataclass
class LidarrSensorEntityDescription(
SensorEntityDescription, LidarrSensorEntityDescriptionMixIn, Generic[T]
SensorEntityDescription, LidarrSensorEntityDescriptionMixIn[T], Generic[T]
):
"""Class to describe a Lidarr sensor."""
attributes_fn: Callable[
[T], dict[str, StateType | datetime] | None
] = lambda _: None
attributes_fn: Callable[[T], dict[str, str] | None] = lambda _: None
description_fn: Callable[
[LidarrSensorEntityDescription, LidarrRootFolder],
tuple[LidarrSensorEntityDescription, str] | None,
] = lambda _, __: None
[LidarrSensorEntityDescription[T], LidarrRootFolder],
tuple[LidarrSensorEntityDescription[T], str] | None,
] | None = None
SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = {
SENSOR_TYPES: dict[str, LidarrSensorEntityDescription[Any]] = {
"disk_space": LidarrSensorEntityDescription(
key="disk_space",
name="Disk space",
@ -78,7 +74,7 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = {
state_class=SensorStateClass.TOTAL,
description_fn=get_modified_description,
),
"queue": LidarrSensorEntityDescription(
"queue": LidarrSensorEntityDescription[LidarrQueue](
key="queue",
name="Queue",
native_unit_of_measurement="Albums",
@ -87,7 +83,7 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription] = {
state_class=SensorStateClass.TOTAL,
attributes_fn=lambda data: {i.title: queue_str(i) for i in data.records},
),
"wanted": LidarrSensorEntityDescription(
"wanted": LidarrSensorEntityDescription[LidarrQueue](
key="wanted",
name="Wanted",
native_unit_of_measurement="Albums",
@ -108,10 +104,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Lidarr sensors based on a config entry."""
coordinators: dict[str, LidarrDataUpdateCoordinator] = hass.data[DOMAIN][
coordinators: dict[str, LidarrDataUpdateCoordinator[Any]] = hass.data[DOMAIN][
entry.entry_id
]
entities = []
entities: list[LidarrSensor[Any]] = []
for coordinator_type, description in SENSOR_TYPES.items():
coordinator = coordinators[coordinator_type]
if coordinator_type != "disk_space":
@ -125,15 +121,15 @@ async def async_setup_entry(
async_add_entities(entities)
class LidarrSensor(LidarrEntity, SensorEntity):
class LidarrSensor(LidarrEntity[T], SensorEntity):
"""Implementation of the Lidarr sensor."""
entity_description: LidarrSensorEntityDescription
entity_description: LidarrSensorEntityDescription[T]
def __init__(
self,
coordinator: LidarrDataUpdateCoordinator,
description: LidarrSensorEntityDescription,
coordinator: LidarrDataUpdateCoordinator[T],
description: LidarrSensorEntityDescription[T],
folder_name: str = "",
) -> None:
"""Create Lidarr entity."""
@ -141,12 +137,12 @@ class LidarrSensor(LidarrEntity, SensorEntity):
self.folder_name = folder_name
@property
def extra_state_attributes(self) -> dict[str, StateType | datetime] | None:
def extra_state_attributes(self) -> dict[str, str] | None:
"""Return the state attributes of the sensor."""
return self.entity_description.attributes_fn(self.coordinator.data)
@property
def native_value(self) -> StateType:
def native_value(self) -> str | int:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data, self.folder_name)

View File

@ -1372,6 +1372,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.lidarr.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.lifx.*]
check_untyped_defs = true
disallow_incomplete_defs = true