Add pylint plugin to check if coordinator is placed in its own module (#108174)

* Add pylint plugin to check if coordinator is placed in its own module

* Remove unintended changes

* Remove pylint disable and let CI only fail on W,E,F

* Make check conventional

* Apply review suggestion

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Use option instead

* Remove pylint arguments from pre-commit

* Partially revert "Remove pylint disable and let CI only fail on W,E,F"

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Jan-Philipp Benecke 2024-01-29 10:30:19 +01:00 committed by GitHub
parent 57622acabf
commit 95aea1488d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 290 additions and 81 deletions

View File

@ -597,14 +597,14 @@ jobs:
run: |
. venv/bin/activate
python --version
pylint --ignore-missing-annotations=y homeassistant
pylint --ignore-missing-annotations=y --ignore-wrong-coordinator-module=y homeassistant
- name: Run pylint (partially)
if: needs.info.outputs.test_full_suite == 'false'
shell: bash
run: |
. venv/bin/activate
python --version
pylint --ignore-missing-annotations=y homeassistant/components/${{ needs.info.outputs.integrations_glob }}
pylint --ignore-missing-annotations=y --ignore-wrong-coordinator-module=y homeassistant/components/${{ needs.info.outputs.integrations_glob }}
mypy:
name: Check mypy

View File

@ -75,7 +75,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
await hass.config_entries.async_reload(entry.entry_id)
class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching AccuWeather data API."""
def __init__(

View File

@ -52,7 +52,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]):
class AuroraAbbDataUpdateCoordinator(DataUpdateCoordinator[dict[str, float]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching AuroraAbbPowerone data."""
def __init__(self, hass: HomeAssistant, comport: str, address: int) -> None:

View File

@ -62,7 +62,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class BrotherDataUpdateCoordinator(DataUpdateCoordinator[BrotherSensors]):
class BrotherDataUpdateCoordinator(DataUpdateCoordinator[BrotherSensors]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Brother data from the printer."""
def __init__(self, hass: HomeAssistant, brother: Brother) -> None:

View File

@ -34,7 +34,7 @@ from .const import DEFAULT_TIMEOUT, DOMAIN
_LOGGER = logging.getLogger(__name__)
class ElmaxCoordinator(DataUpdateCoordinator[PanelStatus]):
class ElmaxCoordinator(DataUpdateCoordinator[PanelStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator helper to handle Elmax API polling."""
def __init__(

View File

@ -99,7 +99,7 @@ def device_info(config_entry: ConfigEntry) -> DeviceInfo:
)
class ECDataUpdateCoordinator(DataUpdateCoordinator):
class ECDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching EC data."""
def __init__(self, hass, ec_data, name, update_interval):

View File

@ -158,7 +158,7 @@ async def async_set_dashboard_info(
await manager.async_set_dashboard_info(addon_slug, host, port)
class ESPHomeDashboard(DataUpdateCoordinator[dict[str, ConfiguredDevice]]):
class ESPHomeDashboard(DataUpdateCoordinator[dict[str, ConfiguredDevice]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to interact with the ESPHome dashboard."""
def __init__(

View File

@ -50,7 +50,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class EvilGeniusUpdateCoordinator(DataUpdateCoordinator[dict]):
class EvilGeniusUpdateCoordinator(DataUpdateCoordinator[dict]): # pylint: disable=hass-enforce-coordinator-module
"""Update coordinator for Evil Genius data."""
info: dict

View File

@ -15,7 +15,7 @@ import homeassistant.util.dt as dt_util
from .const import DOMAIN as FLO_DOMAIN, LOGGER
class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator):
class FloDeviceDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Flo device object."""
def __init__(

View File

@ -179,7 +179,7 @@ class UpdateCoordinatorDataType(TypedDict):
class FritzBoxTools(
update_coordinator.DataUpdateCoordinator[UpdateCoordinatorDataType]
):
): # pylint: disable=hass-enforce-coordinator-module
"""FritzBoxTools class."""
def __init__(
@ -757,7 +757,7 @@ class FritzBoxTools(
raise HomeAssistantError("Service not supported") from ex
class AvmWrapper(FritzBoxTools):
class AvmWrapper(FritzBoxTools): # pylint: disable=hass-enforce-coordinator-module
"""Setup AVM wrapper for API calls."""
async def _async_service_call(

View File

@ -74,7 +74,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class GiosDataUpdateCoordinator(DataUpdateCoordinator[GiosSensors]):
class GiosDataUpdateCoordinator(DataUpdateCoordinator[GiosSensors]): # pylint: disable=hass-enforce-coordinator-module
"""Define an object to hold GIOS data."""
def __init__(

View File

@ -47,7 +47,7 @@ class StateData(NamedTuple):
class DeviceDataUpdateCoordinator(
DataUpdateCoordinator[GogoGate2InfoResponse | ISmartGateInfoResponse]
):
): # pylint: disable=hass-enforce-coordinator-module
"""Manages polling for state changes from the device."""
def __init__(

View File

@ -265,7 +265,7 @@ def _truncate_timeline(timeline: Timeline, max_events: int) -> Timeline:
)
class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]):
class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator for calendar RPC calls that use an efficient sync."""
config_entry: ConfigEntry
@ -320,7 +320,7 @@ class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]):
return None
class CalendarQueryUpdateCoordinator(DataUpdateCoordinator[list[Event]]):
class CalendarQueryUpdateCoordinator(DataUpdateCoordinator[list[Event]]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator for calendar RPC calls.
This sends a polling RPC, not using sync, as a workaround

View File

@ -23,7 +23,7 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
class DeviceDataUpdateCoordinator(DataUpdateCoordinator):
class DeviceDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Manages polling for state changes from the device."""
def __init__(self, hass: HomeAssistant, device: Device) -> None:

View File

@ -746,7 +746,7 @@ def async_remove_addons_from_dev_reg(
dev_reg.async_remove_device(dev.id)
class HassioDataUpdateCoordinator(DataUpdateCoordinator):
class HassioDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Class to retrieve Hass.io status."""
def __init__(

View File

@ -124,7 +124,7 @@ class IntegrationAlert:
return f"{self.filename}_{self.integration}"
class AlertUpdateCoordinator(DataUpdateCoordinator[dict[str, IntegrationAlert]]):
class AlertUpdateCoordinator(DataUpdateCoordinator[dict[str, IntegrationAlert]]): # pylint: disable=hass-enforce-coordinator-module
"""Data fetcher for HA Alerts."""
def __init__(self, hass: HomeAssistant) -> None:

View File

@ -53,7 +53,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class IAlarmDataUpdateCoordinator(DataUpdateCoordinator[None]):
class IAlarmDataUpdateCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching iAlarm data."""
def __init__(self, hass: HomeAssistant, ialarm: IAlarm, mac: str) -> None:

View File

@ -29,7 +29,7 @@ PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.COVER, Platform.SENSOR]
_LOGGER = logging.getLogger(__name__)
class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]):
class IdasenDeskCoordinator(DataUpdateCoordinator[int | None]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage updates for the Idasen Desk."""
def __init__(

View File

@ -158,7 +158,7 @@ class DataUpdateCoordinatorMixin:
return True
class PlenticoreUpdateCoordinator(DataUpdateCoordinator[_DataT]):
class PlenticoreUpdateCoordinator(DataUpdateCoordinator[_DataT]): # pylint: disable=hass-enforce-coordinator-module
"""Base implementation of DataUpdateCoordinator for Plenticore data."""
def __init__(
@ -198,7 +198,7 @@ class PlenticoreUpdateCoordinator(DataUpdateCoordinator[_DataT]):
class ProcessDataUpdateCoordinator(
PlenticoreUpdateCoordinator[Mapping[str, Mapping[str, str]]]
):
): # pylint: disable=hass-enforce-coordinator-module
"""Implementation of PlenticoreUpdateCoordinator for process data."""
async def _async_update_data(self) -> dict[str, dict[str, str]]:
@ -222,7 +222,7 @@ class ProcessDataUpdateCoordinator(
class SettingDataUpdateCoordinator(
PlenticoreUpdateCoordinator[Mapping[str, Mapping[str, str]]],
DataUpdateCoordinatorMixin,
):
): # pylint: disable=hass-enforce-coordinator-module
"""Implementation of PlenticoreUpdateCoordinator for settings data."""
async def _async_update_data(self) -> Mapping[str, Mapping[str, str]]:
@ -237,7 +237,7 @@ class SettingDataUpdateCoordinator(
return fetched_data
class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator[_DataT]):
class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator[_DataT]): # pylint: disable=hass-enforce-coordinator-module
"""Base implementation of DataUpdateCoordinator for Plenticore data."""
def __init__(
@ -284,7 +284,7 @@ class PlenticoreSelectUpdateCoordinator(DataUpdateCoordinator[_DataT]):
class SelectDataUpdateCoordinator(
PlenticoreSelectUpdateCoordinator[dict[str, dict[str, str]]],
DataUpdateCoordinatorMixin,
):
): # pylint: disable=hass-enforce-coordinator-module
"""Implementation of PlenticoreUpdateCoordinator for select data."""
async def _async_update_data(self) -> dict[str, dict[str, str]]:

View File

@ -20,7 +20,7 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class MelnorDataUpdateCoordinator(DataUpdateCoordinator[Device]):
class MelnorDataUpdateCoordinator(DataUpdateCoordinator[Device]): # pylint: disable=hass-enforce-coordinator-module
"""Melnor data update coordinator."""
_device: Device
@ -42,7 +42,7 @@ class MelnorDataUpdateCoordinator(DataUpdateCoordinator[Device]):
return self._device
class MelnorBluetoothEntity(CoordinatorEntity[MelnorDataUpdateCoordinator]):
class MelnorBluetoothEntity(CoordinatorEntity[MelnorDataUpdateCoordinator]): # pylint: disable=hass-enforce-coordinator-module
"""Base class for melnor entities."""
_device: Device

View File

@ -243,7 +243,7 @@ class MikrotikData:
return []
class MikrotikDataUpdateCoordinator(DataUpdateCoordinator[None]):
class MikrotikDataUpdateCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Mikrotik Hub Object."""
def __init__(

View File

@ -21,7 +21,7 @@ _LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR]
class MillDataUpdateCoordinator(DataUpdateCoordinator):
class MillDataUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Mill data."""
def __init__(

View File

@ -98,7 +98,7 @@ def modernforms_exception_handler(
return handler
class ModernFormsDataUpdateCoordinator(DataUpdateCoordinator[ModernFormsDeviceState]):
class ModernFormsDataUpdateCoordinator(DataUpdateCoordinator[ModernFormsDeviceState]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Modern Forms data from single endpoint."""
def __init__(

View File

@ -52,7 +52,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
await hass.config_entries.async_reload(entry.entry_id)
class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]):
class Alpha2BaseCoordinator(DataUpdateCoordinator[dict[str, dict]]): # pylint: disable=hass-enforce-coordinator-module
"""Keep the base instance in one place and centralize the update."""
def __init__(self, hass: HomeAssistant, base: Alpha2Base) -> None:

View File

@ -90,7 +90,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class NAMDataUpdateCoordinator(DataUpdateCoordinator[NAMSensors]):
class NAMDataUpdateCoordinator(DataUpdateCoordinator[NAMSensors]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Nettigo Air Monitor data."""
def __init__(

View File

@ -47,7 +47,7 @@ from .const import (
CoordinatorDataT = TypeVar("CoordinatorDataT", bound=NextDnsData)
class NextDnsUpdateCoordinator(DataUpdateCoordinator[CoordinatorDataT]):
class NextDnsUpdateCoordinator(DataUpdateCoordinator[CoordinatorDataT]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS data API."""
def __init__(
@ -84,7 +84,7 @@ class NextDnsUpdateCoordinator(DataUpdateCoordinator[CoordinatorDataT]):
raise NotImplementedError("Update method not implemented")
class NextDnsStatusUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsStatus]):
class NextDnsStatusUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS analytics status data from API."""
async def _async_update_data_internal(self) -> AnalyticsStatus:
@ -92,7 +92,7 @@ class NextDnsStatusUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsStatus]):
return await self.nextdns.get_analytics_status(self.profile_id)
class NextDnsDnssecUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsDnssec]):
class NextDnsDnssecUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsDnssec]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS analytics Dnssec data from API."""
async def _async_update_data_internal(self) -> AnalyticsDnssec:
@ -100,7 +100,7 @@ class NextDnsDnssecUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsDnssec]):
return await self.nextdns.get_analytics_dnssec(self.profile_id)
class NextDnsEncryptionUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsEncryption]):
class NextDnsEncryptionUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsEncryption]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS analytics encryption data from API."""
async def _async_update_data_internal(self) -> AnalyticsEncryption:
@ -108,7 +108,7 @@ class NextDnsEncryptionUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsEncry
return await self.nextdns.get_analytics_encryption(self.profile_id)
class NextDnsIpVersionsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsIpVersions]):
class NextDnsIpVersionsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsIpVersions]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS analytics IP versions data from API."""
async def _async_update_data_internal(self) -> AnalyticsIpVersions:
@ -116,7 +116,7 @@ class NextDnsIpVersionsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsIpVer
return await self.nextdns.get_analytics_ip_versions(self.profile_id)
class NextDnsProtocolsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsProtocols]):
class NextDnsProtocolsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsProtocols]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS analytics protocols data from API."""
async def _async_update_data_internal(self) -> AnalyticsProtocols:
@ -124,7 +124,7 @@ class NextDnsProtocolsUpdateCoordinator(NextDnsUpdateCoordinator[AnalyticsProtoc
return await self.nextdns.get_analytics_protocols(self.profile_id)
class NextDnsSettingsUpdateCoordinator(NextDnsUpdateCoordinator[Settings]):
class NextDnsSettingsUpdateCoordinator(NextDnsUpdateCoordinator[Settings]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS connection data from API."""
async def _async_update_data_internal(self) -> Settings:
@ -132,7 +132,7 @@ class NextDnsSettingsUpdateCoordinator(NextDnsUpdateCoordinator[Settings]):
return await self.nextdns.get_settings(self.profile_id)
class NextDnsConnectionUpdateCoordinator(NextDnsUpdateCoordinator[ConnectionStatus]):
class NextDnsConnectionUpdateCoordinator(NextDnsUpdateCoordinator[ConnectionStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching NextDNS connection data from API."""
async def _async_update_data_internal(self) -> ConnectionStatus:

View File

@ -279,7 +279,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class NukiCoordinator(DataUpdateCoordinator[None]):
class NukiCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Data Update Coordinator for the Nuki integration."""
def __init__(self, hass, bridge, locks, openers):

View File

@ -45,7 +45,7 @@ class NWSData:
coordinator_forecast_hourly: NwsDataUpdateCoordinator
class NwsDataUpdateCoordinator(TimestampDataUpdateCoordinator[None]):
class NwsDataUpdateCoordinator(TimestampDataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""NWS data update coordinator.
Implements faster data update intervals for failed updates and exposes a last successful update time.

View File

@ -20,7 +20,7 @@ from .const import ALL_ITEM_KINDS, DOMAIN
_LOGGER = logging.getLogger(__name__)
class OmniLogicUpdateCoordinator(DataUpdateCoordinator[dict[tuple, dict[str, Any]]]):
class OmniLogicUpdateCoordinator(DataUpdateCoordinator[dict[tuple, dict[str, Any]]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching update data from single endpoint."""
def __init__(

View File

@ -50,7 +50,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class OpenGarageDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class OpenGarageDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Opengarage data."""
def __init__(

View File

@ -60,7 +60,7 @@ _LOGGER = logging.getLogger(__name__)
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
class WeatherUpdateCoordinator(DataUpdateCoordinator):
class WeatherUpdateCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Weather data update coordinator."""
def __init__(self, owm, latitude, longitude, forecast_mode, hass):

View File

@ -67,7 +67,7 @@ class P1MonitorData(TypedDict):
watermeter: WaterMeter | None
class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]):
class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching P1 Monitor data from single endpoint."""
config_entry: ConfigEntry

View File

@ -85,7 +85,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator to update data."""
config_entry: ConfigEntry

View File

@ -208,7 +208,7 @@ def _device_id(data):
return f"{data.get(ATTR_DEVICE_NAME)}_{data.get(ATTR_DEVICE_ID)}"
class PlaatoCoordinator(DataUpdateCoordinator):
class PlaatoCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching data from the API."""
def __init__(

View File

@ -131,7 +131,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
T = TypeVar("T", PrinterStatus, LegacyPrinterStatus, JobInfo)
class PrusaLinkUpdateCoordinator(DataUpdateCoordinator[T], ABC):
class PrusaLinkUpdateCoordinator(DataUpdateCoordinator[T], ABC): # pylint: disable=hass-enforce-coordinator-module
"""Update coordinator for the printer."""
config_entry: ConfigEntry
@ -176,7 +176,7 @@ class PrusaLinkUpdateCoordinator(DataUpdateCoordinator[T], ABC):
return timedelta(seconds=30)
class StatusCoordinator(PrusaLinkUpdateCoordinator[PrinterStatus]):
class StatusCoordinator(PrusaLinkUpdateCoordinator[PrinterStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Printer update coordinator."""
async def _fetch_data(self) -> PrinterStatus:
@ -184,7 +184,7 @@ class StatusCoordinator(PrusaLinkUpdateCoordinator[PrinterStatus]):
return await self.api.get_status()
class LegacyStatusCoordinator(PrusaLinkUpdateCoordinator[LegacyPrinterStatus]):
class LegacyStatusCoordinator(PrusaLinkUpdateCoordinator[LegacyPrinterStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Printer legacy update coordinator."""
async def _fetch_data(self) -> LegacyPrinterStatus:
@ -192,7 +192,7 @@ class LegacyStatusCoordinator(PrusaLinkUpdateCoordinator[LegacyPrinterStatus]):
return await self.api.get_legacy_printer()
class JobUpdateCoordinator(PrusaLinkUpdateCoordinator[JobInfo]):
class JobUpdateCoordinator(PrusaLinkUpdateCoordinator[JobInfo]): # pylint: disable=hass-enforce-coordinator-module
"""Job update coordinator."""
async def _fetch_data(self) -> JobInfo:
@ -200,7 +200,7 @@ class JobUpdateCoordinator(PrusaLinkUpdateCoordinator[JobInfo]):
return await self.api.get_job()
class PrusaLinkEntity(CoordinatorEntity[PrusaLinkUpdateCoordinator]):
class PrusaLinkEntity(CoordinatorEntity[PrusaLinkUpdateCoordinator]): # pylint: disable=hass-enforce-coordinator-module
"""Defines a base PrusaLink entity."""
_attr_has_entity_name = True

View File

@ -47,7 +47,7 @@ class PureEnergieData(NamedTuple):
smartbridge: SmartBridge
class PureEnergieDataUpdateCoordinator(DataUpdateCoordinator[PureEnergieData]):
class PureEnergieDataUpdateCoordinator(DataUpdateCoordinator[PureEnergieData]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Pure Energie data from single eindpoint."""
config_entry: ConfigEntry

View File

@ -59,7 +59,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class ElecPricesDataUpdateCoordinator(DataUpdateCoordinator[EsiosApiData]):
class ElecPricesDataUpdateCoordinator(DataUpdateCoordinator[EsiosApiData]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Electricity prices data from API."""
def __init__(

View File

@ -87,7 +87,7 @@ async def async_get_type(hass, cloud_id, install_code, host):
return None, None
class EagleDataCoordinator(DataUpdateCoordinator):
class EagleDataCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Get the latest data from the Eagle device."""
eagle100_reader: Eagle100Reader | None = None

View File

@ -84,7 +84,7 @@ def key_exists(data: dict[str, Any], search_key: str) -> bool:
return False
class RainMachineDataUpdateCoordinator(DataUpdateCoordinator[dict]):
class RainMachineDataUpdateCoordinator(DataUpdateCoordinator[dict]): # pylint: disable=hass-enforce-coordinator-module
"""Define an extended DataUpdateCoordinator."""
config_entry: ConfigEntry

View File

@ -197,7 +197,7 @@ async def _update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
await hass.config_entries.async_reload(entry.entry_id)
class RiscoDataUpdateCoordinator(DataUpdateCoordinator[Alarm]):
class RiscoDataUpdateCoordinator(DataUpdateCoordinator[Alarm]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching risco data."""
def __init__(
@ -221,7 +221,7 @@ class RiscoDataUpdateCoordinator(DataUpdateCoordinator[Alarm]):
raise UpdateFailed(error) from error
class RiscoEventsDataUpdateCoordinator(DataUpdateCoordinator[list[Event]]):
class RiscoEventsDataUpdateCoordinator(DataUpdateCoordinator[list[Event]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching risco data."""
def __init__(

View File

@ -20,7 +20,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
from .const import API_TIMEOUT, DOMAIN, LOGGER, UPDATE_INTERVAL
class SharkIqUpdateCoordinator(DataUpdateCoordinator[bool]):
class SharkIqUpdateCoordinator(DataUpdateCoordinator[bool]): # pylint: disable=hass-enforce-coordinator-module
"""Define a wrapper class to update Shark IQ data."""
def __init__(

View File

@ -102,7 +102,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class SurePetcareDataCoordinator(DataUpdateCoordinator[dict[int, SurepyEntity]]):
class SurePetcareDataCoordinator(DataUpdateCoordinator[dict[int, SurepyEntity]]): # pylint: disable=hass-enforce-coordinator-module
"""Handle Surepetcare data."""
def __init__(self, entry: ConfigEntry, hass: HomeAssistant) -> None:

View File

@ -125,7 +125,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class SwitcherDataUpdateCoordinator(
update_coordinator.DataUpdateCoordinator[SwitcherBase]
):
): # pylint: disable=hass-enforce-coordinator-module
"""Switcher device data update coordinator."""
def __init__(

View File

@ -498,7 +498,7 @@ class TibberSensorRT(TibberSensor, CoordinatorEntity["TibberRtDataCoordinator"])
self.async_write_ha_state()
class TibberRtDataCoordinator(DataUpdateCoordinator):
class TibberRtDataCoordinator(DataUpdateCoordinator): # pylint: disable=hass-enforce-coordinator-module
"""Handle Tibber realtime data."""
def __init__(
@ -562,7 +562,7 @@ class TibberRtDataCoordinator(DataUpdateCoordinator):
return self.data.get("data", {}).get("liveMeasurement")
class TibberDataCoordinator(DataUpdateCoordinator[None]):
class TibberDataCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Handle Tibber data and insert statistics."""
config_entry: ConfigEntry

View File

@ -63,7 +63,7 @@ class ToloSaunaData(NamedTuple):
settings: SettingsInfo
class ToloSaunaUpdateCoordinator(DataUpdateCoordinator[ToloSaunaData]):
class ToloSaunaUpdateCoordinator(DataUpdateCoordinator[ToloSaunaData]): # pylint: disable=hass-enforce-coordinator-module
"""DataUpdateCoordinator for TOLO Sauna."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
@ -92,7 +92,7 @@ class ToloSaunaUpdateCoordinator(DataUpdateCoordinator[ToloSaunaData]):
return ToloSaunaData(status, settings)
class ToloSaunaCoordinatorEntity(CoordinatorEntity[ToloSaunaUpdateCoordinator]):
class ToloSaunaCoordinatorEntity(CoordinatorEntity[ToloSaunaUpdateCoordinator]): # pylint: disable=hass-enforce-coordinator-module
"""CoordinatorEntity for TOLO Sauna."""
_attr_has_entity_name = True

View File

@ -163,7 +163,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return unload_ok
class TomorrowioDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class TomorrowioDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): # pylint: disable=hass-enforce-coordinator-module
"""Define an object to hold Tomorrow.io data."""
def __init__(self, hass: HomeAssistant, api: TomorrowioV4) -> None:

View File

@ -78,7 +78,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
client.locations[location_id].auto_bypass_low_battery = bypass
class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator[None]):
class TotalConnectDataUpdateCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Class to fetch data from TotalConnect."""
config_entry: ConfigEntry

View File

@ -15,7 +15,7 @@ POLL_SWITCH_PORT = 300
POLL_GATEWAY = 300
class OmadaSwitchPortCoordinator(OmadaCoordinator[OmadaSwitchPortDetails]):
class OmadaSwitchPortCoordinator(OmadaCoordinator[OmadaSwitchPortDetails]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator for getting details about ports on a switch."""
def __init__(
@ -36,7 +36,7 @@ class OmadaSwitchPortCoordinator(OmadaCoordinator[OmadaSwitchPortDetails]):
return {p.port_id: p for p in ports}
class OmadaGatewayCoordinator(OmadaCoordinator[OmadaGateway]):
class OmadaGatewayCoordinator(OmadaCoordinator[OmadaGateway]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator for getting details about the site's gateway."""
def __init__(

View File

@ -34,7 +34,7 @@ class FirmwareUpdateStatus(NamedTuple):
firmware: OmadaFirmwareUpdate | None
class OmadaFirmwareUpdateCoodinator(OmadaCoordinator[FirmwareUpdateStatus]):
class OmadaFirmwareUpdateCoodinator(OmadaCoordinator[FirmwareUpdateStatus]): # pylint: disable=hass-enforce-coordinator-module
"""Coordinator for getting details about ports on a switch."""
def __init__(self, hass: HomeAssistant, omada_client: OmadaSiteClient) -> None:

View File

@ -46,7 +46,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return unload_ok
class UkraineAlarmDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
class UkraineAlarmDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Ukraine Alarm API."""
def __init__(

View File

@ -57,7 +57,7 @@ STATE_MAP = {"error": STATE_PROBLEM, "started": STATE_ON, "stopped": STATE_OFF}
class UpCloudDataUpdateCoordinator(
DataUpdateCoordinator[dict[str, upcloud_api.Server]]
):
): # pylint: disable=hass-enforce-coordinator-module
"""UpCloud data update coordinator."""
def __init__(

View File

@ -155,7 +155,7 @@ class ValloxState:
return next_filter_change_date
class ValloxDataUpdateCoordinator(DataUpdateCoordinator[ValloxState]):
class ValloxDataUpdateCoordinator(DataUpdateCoordinator[ValloxState]): # pylint: disable=hass-enforce-coordinator-module
"""The DataUpdateCoordinator for Vallox."""

View File

@ -64,7 +64,7 @@ async def async_unload_entry(hass: HomeAssistant, config: ConfigEntry) -> bool:
return unload_ok
class VenstarDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator[None]):
class VenstarDataUpdateCoordinator(update_coordinator.DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching Venstar data."""
def __init__(

View File

@ -97,7 +97,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return unload_ok
class VizioAppsDataUpdateCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]):
class VizioAppsDataUpdateCoordinator(DataUpdateCoordinator[list[dict[str, Any]]]): # pylint: disable=hass-enforce-coordinator-module
"""Define an object to hold Vizio app config data."""
def __init__(self, hass: HomeAssistant, store: Store[list[dict[str, Any]]]) -> None:

View File

@ -168,7 +168,7 @@ class VolvoData:
raise InvalidAuth from exc
class VolvoUpdateCoordinator(DataUpdateCoordinator[None]):
class VolvoUpdateCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Volvo coordinator."""
def __init__(self, hass: HomeAssistant, volvo_data: VolvoData) -> None:

View File

@ -85,7 +85,7 @@ class Options:
)
class DeviceCoordinator(DataUpdateCoordinator[None]):
class DeviceCoordinator(DataUpdateCoordinator[None]): # pylint: disable=hass-enforce-coordinator-module
"""Home Assistant wrapper for a pyWeMo device."""
options: Options | None = None

View File

@ -123,7 +123,7 @@ class XboxData:
presence: dict[str, PresenceData]
class XboxUpdateCoordinator(DataUpdateCoordinator[XboxData]):
class XboxUpdateCoordinator(DataUpdateCoordinator[XboxData]): # pylint: disable=hass-enforce-coordinator-module
"""Store Xbox Console Status."""
def __init__(

View File

@ -104,7 +104,7 @@ async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
await hass.config_entries.async_reload(entry.entry_id)
class MusicCastDataUpdateCoordinator(DataUpdateCoordinator[MusicCastData]):
class MusicCastDataUpdateCoordinator(DataUpdateCoordinator[MusicCastData]): # pylint: disable=hass-enforce-coordinator-module
"""Class to manage fetching data from the API."""
def __init__(self, hass: HomeAssistant, client: MusicCastDevice) -> None:

View File

@ -0,0 +1,54 @@
"""Plugin for checking if coordinator is in its own module."""
from __future__ import annotations
from astroid import nodes
from pylint.checkers import BaseChecker
from pylint.lint import PyLinter
class HassEnforceCoordinatorModule(BaseChecker):
"""Checker for coordinators own module."""
name = "hass_enforce_coordinator_module"
priority = -1
msgs = {
"C7461": (
"Derived data update coordinator is recommended to be placed in the 'coordinator' module",
"hass-enforce-coordinator-module",
"Used when derived data update coordinator should be placed in its own module.",
),
}
options = (
(
"ignore-wrong-coordinator-module",
{
"default": False,
"type": "yn",
"metavar": "<y or n>",
"help": "Set to ``no`` if you wish to check if derived data update coordinator "
"is placed in its own module.",
},
),
)
def visit_classdef(self, node: nodes.ClassDef) -> None:
"""Check if derived data update coordinator is placed in its own module."""
if self.linter.config.ignore_wrong_coordinator_module:
return
root_name = node.root().name
# we only want to check component update coordinators
if not root_name.startswith("homeassistant.components"):
return
is_coordinator_module = root_name.endswith(".coordinator")
for ancestor in node.ancestors():
if ancestor.name == "DataUpdateCoordinator" and not is_coordinator_module:
self.add_message("hass-enforce-coordinator-module", node=node)
return
def register(linter: PyLinter) -> None:
"""Register the checker."""
linter.register_checker(HassEnforceCoordinatorModule(linter))

View File

@ -103,6 +103,7 @@ init-hook = """\
load-plugins = [
"pylint.extensions.code_style",
"pylint.extensions.typing",
"hass_enforce_coordinator_module",
"hass_enforce_sorted_platforms",
"hass_enforce_super_call",
"hass_enforce_type_hints",

View File

@ -101,3 +101,24 @@ def enforce_sorted_platforms_checker_fixture(
)
enforce_sorted_platforms_checker.module = "homeassistant.components.pylint_test"
return enforce_sorted_platforms_checker
@pytest.fixture(name="hass_enforce_coordinator_module", scope="session")
def hass_enforce_coordinator_module_fixture() -> ModuleType:
"""Fixture to the content for the hass_enforce_coordinator_module check."""
return _load_plugin_from_file(
"hass_enforce_coordinator_module",
"pylint/plugins/hass_enforce_coordinator_module.py",
)
@pytest.fixture(name="enforce_coordinator_module_checker")
def enforce_coordinator_module_fixture(
hass_enforce_coordinator_module, linter
) -> BaseChecker:
"""Fixture to provide a hass_enforce_coordinator_module checker."""
enforce_coordinator_module_checker = (
hass_enforce_coordinator_module.HassEnforceCoordinatorModule(linter)
)
enforce_coordinator_module_checker.module = "homeassistant.components.pylint_test"
return enforce_coordinator_module_checker

View File

@ -0,0 +1,133 @@
"""Tests for pylint hass_enforce_coordinator_module plugin."""
from __future__ import annotations
import astroid
from pylint.checkers import BaseChecker
from pylint.interfaces import UNDEFINED
from pylint.testutils import MessageTest
from pylint.testutils.unittest_linter import UnittestLinter
from pylint.utils.ast_walker import ASTWalker
import pytest
from . import assert_adds_messages, assert_no_messages
@pytest.mark.parametrize(
"code",
[
pytest.param(
"""
class DataUpdateCoordinator:
pass
class TestCoordinator(DataUpdateCoordinator):
pass
""",
id="simple",
),
pytest.param(
"""
class DataUpdateCoordinator:
pass
class TestCoordinator(DataUpdateCoordinator):
pass
class TestCoordinator2(TestCoordinator):
pass
""",
id="nested",
),
],
)
def test_enforce_coordinator_module_good(
linter: UnittestLinter, enforce_coordinator_module_checker: BaseChecker, code: str
) -> None:
"""Good test cases."""
root_node = astroid.parse(code, "homeassistant.components.pylint_test.coordinator")
walker = ASTWalker(linter)
walker.add_checker(enforce_coordinator_module_checker)
with assert_no_messages(linter):
walker.walk(root_node)
def test_enforce_coordinator_module_bad_simple(
linter: UnittestLinter,
enforce_coordinator_module_checker: BaseChecker,
) -> None:
"""Bad test case with coordinator extending directly."""
root_node = astroid.parse(
"""
class DataUpdateCoordinator:
pass
class TestCoordinator(DataUpdateCoordinator):
pass
""",
"homeassistant.components.pylint_test",
)
walker = ASTWalker(linter)
walker.add_checker(enforce_coordinator_module_checker)
with assert_adds_messages(
linter,
MessageTest(
msg_id="hass-enforce-coordinator-module",
line=5,
node=root_node.body[1],
args=None,
confidence=UNDEFINED,
col_offset=0,
end_line=5,
end_col_offset=21,
),
):
walker.walk(root_node)
def test_enforce_coordinator_module_bad_nested(
linter: UnittestLinter,
enforce_coordinator_module_checker: BaseChecker,
) -> None:
"""Bad test case with nested coordinators."""
root_node = astroid.parse(
"""
class DataUpdateCoordinator:
pass
class TestCoordinator(DataUpdateCoordinator):
pass
class NopeCoordinator(TestCoordinator):
pass
""",
"homeassistant.components.pylint_test",
)
walker = ASTWalker(linter)
walker.add_checker(enforce_coordinator_module_checker)
with assert_adds_messages(
linter,
MessageTest(
msg_id="hass-enforce-coordinator-module",
line=5,
node=root_node.body[1],
args=None,
confidence=UNDEFINED,
col_offset=0,
end_line=5,
end_col_offset=21,
),
MessageTest(
msg_id="hass-enforce-coordinator-module",
line=8,
node=root_node.body[2],
args=None,
confidence=UNDEFINED,
col_offset=0,
end_line=8,
end_col_offset=21,
),
):
walker.walk(root_node)