From 95aea1488d9d280be6050c776c4a11aa506845ea Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Mon, 29 Jan 2024 10:30:19 +0100 Subject: [PATCH] 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 * 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 --- .github/workflows/ci.yaml | 4 +- .../components/accuweather/__init__.py | 2 +- .../aurora_abb_powerone/__init__.py | 2 +- homeassistant/components/brother/__init__.py | 2 +- homeassistant/components/elmax/common.py | 2 +- .../components/environment_canada/__init__.py | 2 +- homeassistant/components/esphome/dashboard.py | 2 +- .../components/evil_genius_labs/__init__.py | 2 +- homeassistant/components/flo/device.py | 2 +- homeassistant/components/fritz/common.py | 4 +- homeassistant/components/gios/__init__.py | 2 +- homeassistant/components/gogogate2/common.py | 2 +- homeassistant/components/google/calendar.py | 4 +- homeassistant/components/gree/bridge.py | 2 +- homeassistant/components/hassio/__init__.py | 2 +- .../homeassistant_alerts/__init__.py | 2 +- homeassistant/components/ialarm/__init__.py | 2 +- .../components/idasen_desk/__init__.py | 2 +- .../components/kostal_plenticore/helper.py | 10 +- homeassistant/components/melnor/models.py | 4 +- homeassistant/components/mikrotik/hub.py | 2 +- homeassistant/components/mill/__init__.py | 2 +- .../components/modern_forms/__init__.py | 2 +- .../components/moehlenhoff_alpha2/__init__.py | 2 +- homeassistant/components/nam/__init__.py | 2 +- homeassistant/components/nextdns/__init__.py | 16 +-- homeassistant/components/nuki/__init__.py | 2 +- homeassistant/components/nws/__init__.py | 2 +- homeassistant/components/omnilogic/common.py | 2 +- .../components/opengarage/__init__.py | 2 +- .../weather_update_coordinator.py | 2 +- .../components/p1_monitor/__init__.py | 2 +- .../components/philips_js/__init__.py | 2 +- homeassistant/components/plaato/__init__.py | 2 +- .../components/prusalink/__init__.py | 10 +- .../components/pure_energie/__init__.py | 2 +- .../pvpc_hourly_pricing/__init__.py | 2 +- .../components/rainforest_eagle/data.py | 2 +- homeassistant/components/rainmachine/util.py | 2 +- homeassistant/components/risco/__init__.py | 4 +- .../components/sharkiq/update_coordinator.py | 2 +- .../components/surepetcare/__init__.py | 2 +- .../components/switcher_kis/__init__.py | 2 +- homeassistant/components/tibber/sensor.py | 4 +- homeassistant/components/tolo/__init__.py | 4 +- .../components/tomorrowio/__init__.py | 2 +- .../components/totalconnect/__init__.py | 2 +- .../components/tplink_omada/controller.py | 4 +- .../components/tplink_omada/update.py | 2 +- .../components/ukraine_alarm/__init__.py | 2 +- homeassistant/components/upcloud/__init__.py | 2 +- homeassistant/components/vallox/__init__.py | 2 +- homeassistant/components/venstar/__init__.py | 2 +- homeassistant/components/vizio/__init__.py | 2 +- .../components/volvooncall/__init__.py | 2 +- homeassistant/components/wemo/wemo_device.py | 2 +- homeassistant/components/xbox/__init__.py | 2 +- .../components/yamaha_musiccast/__init__.py | 2 +- .../hass_enforce_coordinator_module.py | 54 +++++++ pyproject.toml | 1 + tests/pylint/conftest.py | 21 +++ .../pylint/test_enforce_coordinator_module.py | 133 ++++++++++++++++++ 62 files changed, 290 insertions(+), 81 deletions(-) create mode 100644 pylint/plugins/hass_enforce_coordinator_module.py create mode 100644 tests/pylint/test_enforce_coordinator_module.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3ae6c14940b..0b380400031 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 diff --git a/homeassistant/components/accuweather/__init__.py b/homeassistant/components/accuweather/__init__.py index e98b19e8e82..dfbf5119981 100644 --- a/homeassistant/components/accuweather/__init__.py +++ b/homeassistant/components/accuweather/__init__.py @@ -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__( diff --git a/homeassistant/components/aurora_abb_powerone/__init__.py b/homeassistant/components/aurora_abb_powerone/__init__.py index 39abba4ada5..c7400f31727 100644 --- a/homeassistant/components/aurora_abb_powerone/__init__.py +++ b/homeassistant/components/aurora_abb_powerone/__init__.py @@ -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: diff --git a/homeassistant/components/brother/__init__.py b/homeassistant/components/brother/__init__.py index 27ac97a27dc..32fee44de99 100644 --- a/homeassistant/components/brother/__init__.py +++ b/homeassistant/components/brother/__init__.py @@ -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: diff --git a/homeassistant/components/elmax/common.py b/homeassistant/components/elmax/common.py index 440344fb839..7cbc6f63596 100644 --- a/homeassistant/components/elmax/common.py +++ b/homeassistant/components/elmax/common.py @@ -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__( diff --git a/homeassistant/components/environment_canada/__init__.py b/homeassistant/components/environment_canada/__init__.py index 14fb3e8e54c..925bc42a930 100644 --- a/homeassistant/components/environment_canada/__init__.py +++ b/homeassistant/components/environment_canada/__init__.py @@ -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): diff --git a/homeassistant/components/esphome/dashboard.py b/homeassistant/components/esphome/dashboard.py index 3d7bfef6ddb..03264291d8f 100644 --- a/homeassistant/components/esphome/dashboard.py +++ b/homeassistant/components/esphome/dashboard.py @@ -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__( diff --git a/homeassistant/components/evil_genius_labs/__init__.py b/homeassistant/components/evil_genius_labs/__init__.py index 3d65a5516c7..44d46d27a9d 100644 --- a/homeassistant/components/evil_genius_labs/__init__.py +++ b/homeassistant/components/evil_genius_labs/__init__.py @@ -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 diff --git a/homeassistant/components/flo/device.py b/homeassistant/components/flo/device.py index bcc52f512a1..7aacb1b262a 100644 --- a/homeassistant/components/flo/device.py +++ b/homeassistant/components/flo/device.py @@ -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__( diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index 55bf7279ede..c9acd60b23c 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -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( diff --git a/homeassistant/components/gios/__init__.py b/homeassistant/components/gios/__init__.py index 3cdf48944fd..88c505fc4ae 100644 --- a/homeassistant/components/gios/__init__.py +++ b/homeassistant/components/gios/__init__.py @@ -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__( diff --git a/homeassistant/components/gogogate2/common.py b/homeassistant/components/gogogate2/common.py index ba1426e1201..093c93699ff 100644 --- a/homeassistant/components/gogogate2/common.py +++ b/homeassistant/components/gogogate2/common.py @@ -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__( diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 88f59ff44f7..eb77eb27106 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -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 diff --git a/homeassistant/components/gree/bridge.py b/homeassistant/components/gree/bridge.py index 6628f7fc32c..ebd5e78a820 100644 --- a/homeassistant/components/gree/bridge.py +++ b/homeassistant/components/gree/bridge.py @@ -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: diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index a8e6419a43e..87860644754 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -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__( diff --git a/homeassistant/components/homeassistant_alerts/__init__.py b/homeassistant/components/homeassistant_alerts/__init__.py index 8241c171265..036eb07e067 100644 --- a/homeassistant/components/homeassistant_alerts/__init__.py +++ b/homeassistant/components/homeassistant_alerts/__init__.py @@ -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: diff --git a/homeassistant/components/ialarm/__init__.py b/homeassistant/components/ialarm/__init__.py index b2c1800914e..1b821025953 100644 --- a/homeassistant/components/ialarm/__init__.py +++ b/homeassistant/components/ialarm/__init__.py @@ -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: diff --git a/homeassistant/components/idasen_desk/__init__.py b/homeassistant/components/idasen_desk/__init__.py index 5e112aa39f7..c3e5f3de429 100644 --- a/homeassistant/components/idasen_desk/__init__.py +++ b/homeassistant/components/idasen_desk/__init__.py @@ -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__( diff --git a/homeassistant/components/kostal_plenticore/helper.py b/homeassistant/components/kostal_plenticore/helper.py index adb1bfb6f09..c3228e1d449 100644 --- a/homeassistant/components/kostal_plenticore/helper.py +++ b/homeassistant/components/kostal_plenticore/helper.py @@ -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]]: diff --git a/homeassistant/components/melnor/models.py b/homeassistant/components/melnor/models.py index 409cb9ae3ba..beb8b42a4a3 100644 --- a/homeassistant/components/melnor/models.py +++ b/homeassistant/components/melnor/models.py @@ -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 diff --git a/homeassistant/components/mikrotik/hub.py b/homeassistant/components/mikrotik/hub.py index d03e46a1d0b..44d60d5dcb4 100644 --- a/homeassistant/components/mikrotik/hub.py +++ b/homeassistant/components/mikrotik/hub.py @@ -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__( diff --git a/homeassistant/components/mill/__init__.py b/homeassistant/components/mill/__init__.py index 0482e573766..136c4a2940f 100644 --- a/homeassistant/components/mill/__init__.py +++ b/homeassistant/components/mill/__init__.py @@ -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__( diff --git a/homeassistant/components/modern_forms/__init__.py b/homeassistant/components/modern_forms/__init__.py index 3401d961bc8..5997b2aa846 100644 --- a/homeassistant/components/modern_forms/__init__.py +++ b/homeassistant/components/modern_forms/__init__.py @@ -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__( diff --git a/homeassistant/components/moehlenhoff_alpha2/__init__.py b/homeassistant/components/moehlenhoff_alpha2/__init__.py index fa5db2d0e81..4a4c57b676e 100644 --- a/homeassistant/components/moehlenhoff_alpha2/__init__.py +++ b/homeassistant/components/moehlenhoff_alpha2/__init__.py @@ -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: diff --git a/homeassistant/components/nam/__init__.py b/homeassistant/components/nam/__init__.py index d5881f52d8d..28f9c282a73 100644 --- a/homeassistant/components/nam/__init__.py +++ b/homeassistant/components/nam/__init__.py @@ -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__( diff --git a/homeassistant/components/nextdns/__init__.py b/homeassistant/components/nextdns/__init__.py index 011b487910f..ca59c7d0e3a 100644 --- a/homeassistant/components/nextdns/__init__.py +++ b/homeassistant/components/nextdns/__init__.py @@ -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: diff --git a/homeassistant/components/nuki/__init__.py b/homeassistant/components/nuki/__init__.py index 3f17c0b795b..42d95f85937 100644 --- a/homeassistant/components/nuki/__init__.py +++ b/homeassistant/components/nuki/__init__.py @@ -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): diff --git a/homeassistant/components/nws/__init__.py b/homeassistant/components/nws/__init__.py index 063ecdabab2..da54f3b119e 100644 --- a/homeassistant/components/nws/__init__.py +++ b/homeassistant/components/nws/__init__.py @@ -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. diff --git a/homeassistant/components/omnilogic/common.py b/homeassistant/components/omnilogic/common.py index 4e64a219f77..3fbd53d20f2 100644 --- a/homeassistant/components/omnilogic/common.py +++ b/homeassistant/components/omnilogic/common.py @@ -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__( diff --git a/homeassistant/components/opengarage/__init__.py b/homeassistant/components/opengarage/__init__.py index b825cace83a..46d018ec1af 100644 --- a/homeassistant/components/opengarage/__init__.py +++ b/homeassistant/components/opengarage/__init__.py @@ -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__( diff --git a/homeassistant/components/openweathermap/weather_update_coordinator.py b/homeassistant/components/openweathermap/weather_update_coordinator.py index 56519c46fd9..05b24d60f79 100644 --- a/homeassistant/components/openweathermap/weather_update_coordinator.py +++ b/homeassistant/components/openweathermap/weather_update_coordinator.py @@ -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): diff --git a/homeassistant/components/p1_monitor/__init__.py b/homeassistant/components/p1_monitor/__init__.py index e6178ffeb41..18c58525097 100644 --- a/homeassistant/components/p1_monitor/__init__.py +++ b/homeassistant/components/p1_monitor/__init__.py @@ -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 diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py index 3fce7f1fafd..c8540a187da 100644 --- a/homeassistant/components/philips_js/__init__.py +++ b/homeassistant/components/philips_js/__init__.py @@ -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 diff --git a/homeassistant/components/plaato/__init__.py b/homeassistant/components/plaato/__init__.py index aeb1cea8e15..69c65383138 100644 --- a/homeassistant/components/plaato/__init__.py +++ b/homeassistant/components/plaato/__init__.py @@ -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__( diff --git a/homeassistant/components/prusalink/__init__.py b/homeassistant/components/prusalink/__init__.py index b6a00bbaf10..94cf21e13df 100644 --- a/homeassistant/components/prusalink/__init__.py +++ b/homeassistant/components/prusalink/__init__.py @@ -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 diff --git a/homeassistant/components/pure_energie/__init__.py b/homeassistant/components/pure_energie/__init__.py index 4a64e5abb84..cda73a7da0b 100644 --- a/homeassistant/components/pure_energie/__init__.py +++ b/homeassistant/components/pure_energie/__init__.py @@ -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 diff --git a/homeassistant/components/pvpc_hourly_pricing/__init__.py b/homeassistant/components/pvpc_hourly_pricing/__init__.py index 00a3a355477..af9154f5512 100644 --- a/homeassistant/components/pvpc_hourly_pricing/__init__.py +++ b/homeassistant/components/pvpc_hourly_pricing/__init__.py @@ -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__( diff --git a/homeassistant/components/rainforest_eagle/data.py b/homeassistant/components/rainforest_eagle/data.py index f050e92f783..9da6372086f 100644 --- a/homeassistant/components/rainforest_eagle/data.py +++ b/homeassistant/components/rainforest_eagle/data.py @@ -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 diff --git a/homeassistant/components/rainmachine/util.py b/homeassistant/components/rainmachine/util.py index 64917b6d721..dfb03b11b5d 100644 --- a/homeassistant/components/rainmachine/util.py +++ b/homeassistant/components/rainmachine/util.py @@ -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 diff --git a/homeassistant/components/risco/__init__.py b/homeassistant/components/risco/__init__.py index c58721e4e28..d1e1c4f430c 100644 --- a/homeassistant/components/risco/__init__.py +++ b/homeassistant/components/risco/__init__.py @@ -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__( diff --git a/homeassistant/components/sharkiq/update_coordinator.py b/homeassistant/components/sharkiq/update_coordinator.py index 4cfbb033566..e1330b06c08 100644 --- a/homeassistant/components/sharkiq/update_coordinator.py +++ b/homeassistant/components/sharkiq/update_coordinator.py @@ -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__( diff --git a/homeassistant/components/surepetcare/__init__.py b/homeassistant/components/surepetcare/__init__.py index 0ef47a488df..d4c337c4096 100644 --- a/homeassistant/components/surepetcare/__init__.py +++ b/homeassistant/components/surepetcare/__init__.py @@ -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: diff --git a/homeassistant/components/switcher_kis/__init__.py b/homeassistant/components/switcher_kis/__init__.py index 051c5d2b72a..79ef201efee 100644 --- a/homeassistant/components/switcher_kis/__init__.py +++ b/homeassistant/components/switcher_kis/__init__.py @@ -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__( diff --git a/homeassistant/components/tibber/sensor.py b/homeassistant/components/tibber/sensor.py index 2694ef50e3a..467cd2bfd77 100644 --- a/homeassistant/components/tibber/sensor.py +++ b/homeassistant/components/tibber/sensor.py @@ -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 diff --git a/homeassistant/components/tolo/__init__.py b/homeassistant/components/tolo/__init__.py index f0cf94bb825..2fc41fac3af 100644 --- a/homeassistant/components/tolo/__init__.py +++ b/homeassistant/components/tolo/__init__.py @@ -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 diff --git a/homeassistant/components/tomorrowio/__init__.py b/homeassistant/components/tomorrowio/__init__.py index 25b814c106a..ea179219153 100644 --- a/homeassistant/components/tomorrowio/__init__.py +++ b/homeassistant/components/tomorrowio/__init__.py @@ -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: diff --git a/homeassistant/components/totalconnect/__init__.py b/homeassistant/components/totalconnect/__init__.py index 967cbfa7e73..e10858c6c12 100644 --- a/homeassistant/components/totalconnect/__init__.py +++ b/homeassistant/components/totalconnect/__init__.py @@ -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 diff --git a/homeassistant/components/tplink_omada/controller.py b/homeassistant/components/tplink_omada/controller.py index 194f18ae9bf..be9e875037e 100644 --- a/homeassistant/components/tplink_omada/controller.py +++ b/homeassistant/components/tplink_omada/controller.py @@ -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__( diff --git a/homeassistant/components/tplink_omada/update.py b/homeassistant/components/tplink_omada/update.py index 1e653a53aae..a5f54071c4f 100644 --- a/homeassistant/components/tplink_omada/update.py +++ b/homeassistant/components/tplink_omada/update.py @@ -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: diff --git a/homeassistant/components/ukraine_alarm/__init__.py b/homeassistant/components/ukraine_alarm/__init__.py index eb24e5d9a78..1132bd56b72 100644 --- a/homeassistant/components/ukraine_alarm/__init__.py +++ b/homeassistant/components/ukraine_alarm/__init__.py @@ -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__( diff --git a/homeassistant/components/upcloud/__init__.py b/homeassistant/components/upcloud/__init__.py index a2554858fef..49ec97f073b 100644 --- a/homeassistant/components/upcloud/__init__.py +++ b/homeassistant/components/upcloud/__init__.py @@ -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__( diff --git a/homeassistant/components/vallox/__init__.py b/homeassistant/components/vallox/__init__.py index c98e2685118..3808bfb1202 100644 --- a/homeassistant/components/vallox/__init__.py +++ b/homeassistant/components/vallox/__init__.py @@ -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.""" diff --git a/homeassistant/components/venstar/__init__.py b/homeassistant/components/venstar/__init__.py index 1416bcf376a..78cb20b33cc 100644 --- a/homeassistant/components/venstar/__init__.py +++ b/homeassistant/components/venstar/__init__.py @@ -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__( diff --git a/homeassistant/components/vizio/__init__.py b/homeassistant/components/vizio/__init__.py index af9e649a8b0..2e468087725 100644 --- a/homeassistant/components/vizio/__init__.py +++ b/homeassistant/components/vizio/__init__.py @@ -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: diff --git a/homeassistant/components/volvooncall/__init__.py b/homeassistant/components/volvooncall/__init__.py index 4ec1bf4a4ba..8bade56fa97 100644 --- a/homeassistant/components/volvooncall/__init__.py +++ b/homeassistant/components/volvooncall/__init__.py @@ -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: diff --git a/homeassistant/components/wemo/wemo_device.py b/homeassistant/components/wemo/wemo_device.py index 110943a6503..2c216100244 100644 --- a/homeassistant/components/wemo/wemo_device.py +++ b/homeassistant/components/wemo/wemo_device.py @@ -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 diff --git a/homeassistant/components/xbox/__init__.py b/homeassistant/components/xbox/__init__.py index 7f1f11ba25d..37e11dd2693 100644 --- a/homeassistant/components/xbox/__init__.py +++ b/homeassistant/components/xbox/__init__.py @@ -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__( diff --git a/homeassistant/components/yamaha_musiccast/__init__.py b/homeassistant/components/yamaha_musiccast/__init__.py index 307171487bc..5242aa90819 100644 --- a/homeassistant/components/yamaha_musiccast/__init__.py +++ b/homeassistant/components/yamaha_musiccast/__init__.py @@ -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: diff --git a/pylint/plugins/hass_enforce_coordinator_module.py b/pylint/plugins/hass_enforce_coordinator_module.py new file mode 100644 index 00000000000..3546632547b --- /dev/null +++ b/pylint/plugins/hass_enforce_coordinator_module.py @@ -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": "", + "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)) diff --git a/pyproject.toml b/pyproject.toml index 6ed57860ee7..535cac1292f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", diff --git a/tests/pylint/conftest.py b/tests/pylint/conftest.py index 3554dc66c92..2aeb5fbd5b7 100644 --- a/tests/pylint/conftest.py +++ b/tests/pylint/conftest.py @@ -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 diff --git a/tests/pylint/test_enforce_coordinator_module.py b/tests/pylint/test_enforce_coordinator_module.py new file mode 100644 index 00000000000..746da8c1d7e --- /dev/null +++ b/tests/pylint/test_enforce_coordinator_module.py @@ -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)