Add strict typing for radarr (#79242)

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

View file

@ -210,6 +210,7 @@ homeassistant.components.pvoutput.*
homeassistant.components.qnap_qsw.* homeassistant.components.qnap_qsw.*
homeassistant.components.rainmachine.* homeassistant.components.rainmachine.*
homeassistant.components.rdw.* homeassistant.components.rdw.*
homeassistant.components.radarr.*
homeassistant.components.recollect_waste.* homeassistant.components.recollect_waste.*
homeassistant.components.recorder.* homeassistant.components.recorder.*
homeassistant.components.remote.* homeassistant.components.remote.*

View file

@ -1,6 +1,8 @@
"""The Radarr component.""" """The Radarr component."""
from __future__ import annotations from __future__ import annotations
from typing import Any, cast
from aiopyarr.models.host_configuration import PyArrHostConfiguration from aiopyarr.models.host_configuration import PyArrHostConfiguration
from aiopyarr.radarr_client import RadarrClient from aiopyarr.radarr_client import RadarrClient
@ -29,6 +31,7 @@ from .coordinator import (
MoviesDataUpdateCoordinator, MoviesDataUpdateCoordinator,
RadarrDataUpdateCoordinator, RadarrDataUpdateCoordinator,
StatusDataUpdateCoordinator, StatusDataUpdateCoordinator,
T,
) )
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
@ -65,7 +68,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
host_configuration=host_configuration, host_configuration=host_configuration,
session=async_get_clientsession(hass, entry.data[CONF_VERIFY_SSL]), session=async_get_clientsession(hass, entry.data[CONF_VERIFY_SSL]),
) )
coordinators: dict[str, RadarrDataUpdateCoordinator] = { coordinators: dict[str, RadarrDataUpdateCoordinator[Any]] = {
"status": StatusDataUpdateCoordinator(hass, host_configuration, radarr), "status": StatusDataUpdateCoordinator(hass, host_configuration, radarr),
"disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr), "disk_space": DiskSpaceDataUpdateCoordinator(hass, host_configuration, radarr),
"health": HealthDataUpdateCoordinator(hass, host_configuration, radarr), "health": HealthDataUpdateCoordinator(hass, host_configuration, radarr),
@ -86,15 +89,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok return unload_ok
class RadarrEntity(CoordinatorEntity[RadarrDataUpdateCoordinator]): class RadarrEntity(CoordinatorEntity[RadarrDataUpdateCoordinator[T]]):
"""Defines a base Radarr entity.""" """Defines a base Radarr entity."""
_attr_has_entity_name = True _attr_has_entity_name = True
coordinator: RadarrDataUpdateCoordinator coordinator: RadarrDataUpdateCoordinator[T]
def __init__( def __init__(
self, self,
coordinator: RadarrDataUpdateCoordinator, coordinator: RadarrDataUpdateCoordinator[T],
description: EntityDescription, description: EntityDescription,
) -> None: ) -> None:
"""Create Radarr entity.""" """Create Radarr entity."""
@ -113,5 +116,7 @@ class RadarrEntity(CoordinatorEntity[RadarrDataUpdateCoordinator]):
name=self.coordinator.config_entry.title, name=self.coordinator.config_entry.title,
) )
if isinstance(self.coordinator, StatusDataUpdateCoordinator): if isinstance(self.coordinator, StatusDataUpdateCoordinator):
device_info[ATTR_SW_VERSION] = self.coordinator.data.version device_info[ATTR_SW_VERSION] = cast(
StatusDataUpdateCoordinator, self.coordinator
).data.version
return device_info return device_info

View file

@ -1,6 +1,8 @@
"""Support for Radarr binary sensors.""" """Support for Radarr binary sensors."""
from __future__ import annotations from __future__ import annotations
from aiopyarr import Health
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass, BinarySensorDeviceClass,
BinarySensorEntity, BinarySensorEntity,
@ -32,7 +34,7 @@ async def async_setup_entry(
async_add_entities([RadarrBinarySensor(coordinator, BINARY_SENSOR_TYPE)]) async_add_entities([RadarrBinarySensor(coordinator, BINARY_SENSOR_TYPE)])
class RadarrBinarySensor(RadarrEntity, BinarySensorEntity): class RadarrBinarySensor(RadarrEntity[list[Health]], BinarySensorEntity):
"""Implementation of a Radarr binary sensor.""" """Implementation of a Radarr binary sensor."""
@property @property

View file

@ -3,9 +3,9 @@ from __future__ import annotations
from abc import abstractmethod from abc import abstractmethod
from datetime import timedelta from datetime import timedelta
from typing import Generic, TypeVar, cast from typing import Generic, TypeVar, Union, cast
from aiopyarr import Health, RootFolder, SystemStatus, exceptions from aiopyarr import Health, RadarrMovie, RootFolder, SystemStatus, exceptions
from aiopyarr.models.host_configuration import PyArrHostConfiguration from aiopyarr.models.host_configuration import PyArrHostConfiguration
from aiopyarr.radarr_client import RadarrClient from aiopyarr.radarr_client import RadarrClient
@ -16,10 +16,10 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import DOMAIN, LOGGER from .const import DOMAIN, LOGGER
T = TypeVar("T", SystemStatus, list[RootFolder], list[Health], int) T = TypeVar("T", bound=Union[SystemStatus, list[RootFolder], list[Health], int])
class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]): class RadarrDataUpdateCoordinator(DataUpdateCoordinator[T], Generic[T]):
"""Data update coordinator for the Radarr integration.""" """Data update coordinator for the Radarr integration."""
config_entry: ConfigEntry config_entry: ConfigEntry
@ -58,7 +58,7 @@ class RadarrDataUpdateCoordinator(DataUpdateCoordinator, Generic[T]):
raise NotImplementedError raise NotImplementedError
class StatusDataUpdateCoordinator(RadarrDataUpdateCoordinator): class StatusDataUpdateCoordinator(RadarrDataUpdateCoordinator[SystemStatus]):
"""Status update coordinator for Radarr.""" """Status update coordinator for Radarr."""
async def _fetch_data(self) -> SystemStatus: async def _fetch_data(self) -> SystemStatus:
@ -66,15 +66,15 @@ class StatusDataUpdateCoordinator(RadarrDataUpdateCoordinator):
return await self.api_client.async_get_system_status() return await self.api_client.async_get_system_status()
class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator): class DiskSpaceDataUpdateCoordinator(RadarrDataUpdateCoordinator[list[RootFolder]]):
"""Disk space update coordinator for Radarr.""" """Disk space update coordinator for Radarr."""
async def _fetch_data(self) -> list[RootFolder]: async def _fetch_data(self) -> list[RootFolder]:
"""Fetch the data.""" """Fetch the data."""
return cast(list, await self.api_client.async_get_root_folders()) return cast(list[RootFolder], await self.api_client.async_get_root_folders())
class HealthDataUpdateCoordinator(RadarrDataUpdateCoordinator): class HealthDataUpdateCoordinator(RadarrDataUpdateCoordinator[list[Health]]):
"""Health update coordinator.""" """Health update coordinator."""
async def _fetch_data(self) -> list[Health]: async def _fetch_data(self) -> list[Health]:
@ -82,9 +82,9 @@ class HealthDataUpdateCoordinator(RadarrDataUpdateCoordinator):
return await self.api_client.async_get_failed_health_checks() return await self.api_client.async_get_failed_health_checks()
class MoviesDataUpdateCoordinator(RadarrDataUpdateCoordinator): class MoviesDataUpdateCoordinator(RadarrDataUpdateCoordinator[int]):
"""Movies update coordinator.""" """Movies update coordinator."""
async def _fetch_data(self) -> int: async def _fetch_data(self) -> int:
"""Fetch the movies data.""" """Fetch the movies data."""
return len(cast(list, await self.api_client.async_get_movies())) return len(cast(list[RadarrMovie], await self.api_client.async_get_movies()))

View file

@ -4,10 +4,10 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from copy import deepcopy from copy import deepcopy
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timezone from datetime import datetime, timezone
from typing import Generic from typing import Any, Generic
from aiopyarr import Diskspace, RootFolder from aiopyarr import Diskspace, RootFolder, SystemStatus
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
@ -32,7 +32,7 @@ from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import RadarrEntity from . import RadarrEntity
from .const import DOMAIN from .const import DOMAIN
@ -50,8 +50,8 @@ def get_space(data: list[Diskspace], name: str) -> str:
def get_modified_description( def get_modified_description(
description: RadarrSensorEntityDescription, mount: RootFolder description: RadarrSensorEntityDescription[T], mount: RootFolder
) -> tuple[RadarrSensorEntityDescription, str]: ) -> tuple[RadarrSensorEntityDescription[T], str]:
"""Return modified description and folder name.""" """Return modified description and folder name."""
desc = deepcopy(description) desc = deepcopy(description)
name = mount.path.rsplit("/")[-1].rsplit("\\")[-1] name = mount.path.rsplit("/")[-1].rsplit("\\")[-1]
@ -64,7 +64,7 @@ def get_modified_description(
class RadarrSensorEntityDescriptionMixIn(Generic[T]): class RadarrSensorEntityDescriptionMixIn(Generic[T]):
"""Mixin for required keys.""" """Mixin for required keys."""
value_fn: Callable[[T, str], str] value_fn: Callable[[T, str], str | int | datetime]
@dataclass @dataclass
@ -74,12 +74,12 @@ class RadarrSensorEntityDescription(
"""Class to describe a Radarr sensor.""" """Class to describe a Radarr sensor."""
description_fn: Callable[ description_fn: Callable[
[RadarrSensorEntityDescription, RootFolder], [RadarrSensorEntityDescription[T], RootFolder],
tuple[RadarrSensorEntityDescription, str] | None, tuple[RadarrSensorEntityDescription[T], str] | None,
] = lambda _, __: None ] | None = None
SENSOR_TYPES: dict[str, RadarrSensorEntityDescription] = { SENSOR_TYPES: dict[str, RadarrSensorEntityDescription[Any]] = {
"disk_space": RadarrSensorEntityDescription( "disk_space": RadarrSensorEntityDescription(
key="disk_space", key="disk_space",
name="Disk space", name="Disk space",
@ -88,7 +88,7 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription] = {
value_fn=get_space, value_fn=get_space,
description_fn=get_modified_description, description_fn=get_modified_description,
), ),
"movie": RadarrSensorEntityDescription( "movie": RadarrSensorEntityDescription[int](
key="movies", key="movies",
name="Movies", name="Movies",
native_unit_of_measurement="Movies", native_unit_of_measurement="Movies",
@ -96,7 +96,7 @@ SENSOR_TYPES: dict[str, RadarrSensorEntityDescription] = {
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
value_fn=lambda data, _: data, value_fn=lambda data, _: data,
), ),
"status": RadarrSensorEntityDescription( "status": RadarrSensorEntityDescription[SystemStatus](
key="start_time", key="start_time",
name="Start time", name="Start time",
device_class=SensorDeviceClass.TIMESTAMP, device_class=SensorDeviceClass.TIMESTAMP,
@ -152,10 +152,10 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up Radarr sensors based on a config entry.""" """Set up Radarr sensors based on a config entry."""
coordinators: dict[str, RadarrDataUpdateCoordinator] = hass.data[DOMAIN][ coordinators: dict[str, RadarrDataUpdateCoordinator[Any]] = hass.data[DOMAIN][
entry.entry_id entry.entry_id
] ]
entities = [] entities: list[RadarrSensor[Any]] = []
for coordinator_type, description in SENSOR_TYPES.items(): for coordinator_type, description in SENSOR_TYPES.items():
coordinator = coordinators[coordinator_type] coordinator = coordinators[coordinator_type]
if coordinator_type != "disk_space": if coordinator_type != "disk_space":
@ -169,16 +169,16 @@ async def async_setup_entry(
async_add_entities(entities) async_add_entities(entities)
class RadarrSensor(RadarrEntity, SensorEntity): class RadarrSensor(RadarrEntity[T], SensorEntity):
"""Implementation of the Radarr sensor.""" """Implementation of the Radarr sensor."""
coordinator: RadarrDataUpdateCoordinator coordinator: RadarrDataUpdateCoordinator[T]
entity_description: RadarrSensorEntityDescription entity_description: RadarrSensorEntityDescription[T]
def __init__( def __init__(
self, self,
coordinator: RadarrDataUpdateCoordinator, coordinator: RadarrDataUpdateCoordinator[T],
description: RadarrSensorEntityDescription, description: RadarrSensorEntityDescription[T],
folder_name: str = "", folder_name: str = "",
) -> None: ) -> None:
"""Create Radarr entity.""" """Create Radarr entity."""
@ -186,6 +186,6 @@ class RadarrSensor(RadarrEntity, SensorEntity):
self.folder_name = folder_name self.folder_name = folder_name
@property @property
def native_value(self) -> StateType: def native_value(self) -> str | int | datetime:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data, self.folder_name) return self.entity_description.value_fn(self.coordinator.data, self.folder_name)

View file

@ -1852,6 +1852,16 @@ disallow_untyped_defs = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.radarr.*]
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.recollect_waste.*] [mypy-homeassistant.components.recollect_waste.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true