mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Add base Entity class to enforce-class-module pylint plugin (#126026)
* Add base Entity class to enforcé-class-module pylint plugin * Ignore bluetooth * Ignore hue * Ignore dominos * Ignore ffmpeg * Ignore mqtt * Ignore microsoft_face * Ignore plant * Ignore point * Ignore rfxtrx * Ignore template * Ignore tag * Ignore deconz
This commit is contained in:
parent
5fcdcbf9b9
commit
6bc2d11c5e
@ -597,6 +597,7 @@ class PassiveBluetoothDataProcessor[_T, _DataT]:
|
|||||||
self.async_update_listeners(new_data, was_available, changed_entity_keys)
|
self.async_update_listeners(new_data, was_available, changed_entity_keys)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-enforce-class-module
|
||||||
class PassiveBluetoothProcessorEntity[
|
class PassiveBluetoothProcessorEntity[
|
||||||
_PassiveBluetoothDataProcessorT: PassiveBluetoothDataProcessor[Any, Any]
|
_PassiveBluetoothDataProcessorT: PassiveBluetoothDataProcessor[Any, Any]
|
||||||
](Entity):
|
](Entity):
|
||||||
|
@ -68,6 +68,7 @@ class DeconzBase[_DeviceT: _DeviceType]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-enforce-class-module
|
||||||
class DeconzDevice[_DeviceT: _DeviceType](DeconzBase[_DeviceT], Entity):
|
class DeconzDevice[_DeviceT: _DeviceType](DeconzBase[_DeviceT], Entity):
|
||||||
"""Representation of a deCONZ device."""
|
"""Representation of a deCONZ device."""
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ class DominosProductListView(http.HomeAssistantView):
|
|||||||
return self.json(self.dominos.get_menu())
|
return self.json(self.dominos.get_menu())
|
||||||
|
|
||||||
|
|
||||||
class DominosOrder(Entity):
|
class DominosOrder(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Represents a Dominos order entity."""
|
"""Represents a Dominos order entity."""
|
||||||
|
|
||||||
def __init__(self, order_info, dominos):
|
def __init__(self, order_info, dominos):
|
||||||
|
@ -176,7 +176,7 @@ class FFmpegManager:
|
|||||||
return CONTENT_TYPE_MULTIPART.format("ffserver")
|
return CONTENT_TYPE_MULTIPART.format("ffserver")
|
||||||
|
|
||||||
|
|
||||||
class FFmpegBase[_HAFFmpegT: HAFFmpeg](Entity):
|
class FFmpegBase[_HAFFmpegT: HAFFmpeg](Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Interface object for FFmpeg."""
|
"""Interface object for FFmpeg."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
@ -165,7 +165,7 @@ class SensorManager:
|
|||||||
self._component_add_entities[platform](value)
|
self._component_add_entities[platform](value)
|
||||||
|
|
||||||
|
|
||||||
class GenericHueSensor(GenericHueDevice, entity.Entity):
|
class GenericHueSensor(GenericHueDevice, entity.Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Representation of a Hue sensor."""
|
"""Representation of a Hue sensor."""
|
||||||
|
|
||||||
should_poll = False
|
should_poll = False
|
||||||
|
@ -10,7 +10,7 @@ from ..const import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GenericHueDevice(entity.Entity):
|
class GenericHueDevice(entity.Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Representation of a Hue device."""
|
"""Representation of a Hue device."""
|
||||||
|
|
||||||
def __init__(self, sensor, name, bridge, primary_sensor=None):
|
def __init__(self, sensor, name, bridge, primary_sensor=None):
|
||||||
|
@ -34,7 +34,7 @@ RESOURCE_TYPE_NAMES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HueBaseEntity(Entity):
|
class HueBaseEntity(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Generic Entity Class for a Hue resource."""
|
"""Generic Entity Class for a Hue resource."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
@ -214,7 +214,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class MicrosoftFaceGroupEntity(Entity):
|
class MicrosoftFaceGroupEntity(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Person-Group state/data Entity."""
|
"""Person-Group state/data Entity."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
@ -369,7 +369,7 @@ def init_entity_id_from_config(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MqttAttributesMixin(Entity):
|
class MqttAttributesMixin(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Mixin used for platforms that support JSON attributes."""
|
"""Mixin used for platforms that support JSON attributes."""
|
||||||
|
|
||||||
_attributes_extra_blocked: frozenset[str] = frozenset()
|
_attributes_extra_blocked: frozenset[str] = frozenset()
|
||||||
@ -454,7 +454,7 @@ class MqttAttributesMixin(Entity):
|
|||||||
_LOGGER.warning("JSON result was not a dictionary")
|
_LOGGER.warning("JSON result was not a dictionary")
|
||||||
|
|
||||||
|
|
||||||
class MqttAvailabilityMixin(Entity):
|
class MqttAvailabilityMixin(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Mixin used for platforms that report availability."""
|
"""Mixin used for platforms that report availability."""
|
||||||
|
|
||||||
def __init__(self, config: ConfigType) -> None:
|
def __init__(self, config: ConfigType) -> None:
|
||||||
@ -799,7 +799,7 @@ class MqttDiscoveryDeviceUpdateMixin(ABC):
|
|||||||
"""Handle the cleanup of platform specific parts, extend to the platform."""
|
"""Handle the cleanup of platform specific parts, extend to the platform."""
|
||||||
|
|
||||||
|
|
||||||
class MqttDiscoveryUpdateMixin(Entity):
|
class MqttDiscoveryUpdateMixin(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Mixin used to handle updated discovery message for entity based platforms."""
|
"""Mixin used to handle updated discovery message for entity based platforms."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -1021,7 +1021,7 @@ def device_info_from_specifications(
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
class MqttEntityDeviceInfo(Entity):
|
class MqttEntityDeviceInfo(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Mixin used for mqtt platforms that support the device registry."""
|
"""Mixin used for mqtt platforms that support the device registry."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -127,7 +127,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Plant(Entity):
|
class Plant(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Plant monitors the well-being of a plant.
|
"""Plant monitors the well-being of a plant.
|
||||||
|
|
||||||
It also checks the measurements against
|
It also checks the measurements against
|
||||||
|
@ -257,7 +257,7 @@ class MinutPointClient:
|
|||||||
return await self._client.alarm_arm(home_id)
|
return await self._client.alarm_arm(home_id)
|
||||||
|
|
||||||
|
|
||||||
class MinutPointEntity(Entity):
|
class MinutPointEntity(Entity): # pylint: disable=hass-enforce-class-module # see PR 118243
|
||||||
"""Base Entity used by the sensors."""
|
"""Base Entity used by the sensors."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
|
@ -93,7 +93,7 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RfxtrxOffDelayMixin(Entity):
|
class RfxtrxOffDelayMixin(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Mixin to support timeouts on data.
|
"""Mixin to support timeouts on data.
|
||||||
|
|
||||||
Many 433 devices only send data when active. They will
|
Many 433 devices only send data when active. They will
|
||||||
|
@ -360,7 +360,7 @@ async def async_scan_tag(
|
|||||||
_LOGGER.debug("Tag: %s scanned by device: %s", tag_id, device_id)
|
_LOGGER.debug("Tag: %s scanned by device: %s", tag_id, device_id)
|
||||||
|
|
||||||
|
|
||||||
class TagEntity(Entity):
|
class TagEntity(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Representation of a Tag entity."""
|
"""Representation of a Tag entity."""
|
||||||
|
|
||||||
_unrecorded_attributes = frozenset({TAG_ID})
|
_unrecorded_attributes = frozenset({TAG_ID})
|
||||||
|
@ -244,7 +244,7 @@ class _TemplateAttribute:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
class TemplateEntity(Entity):
|
class TemplateEntity(Entity): # pylint: disable=hass-enforce-class-module
|
||||||
"""Entity that uses templates to calculate attributes."""
|
"""Entity that uses templates to calculate attributes."""
|
||||||
|
|
||||||
_attr_available = True
|
_attr_available = True
|
||||||
|
@ -8,6 +8,8 @@ from astroid import nodes
|
|||||||
from pylint.checkers import BaseChecker
|
from pylint.checkers import BaseChecker
|
||||||
from pylint.lint import PyLinter
|
from pylint.lint import PyLinter
|
||||||
|
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
|
||||||
_MODULES: dict[str, set[str]] = {
|
_MODULES: dict[str, set[str]] = {
|
||||||
"air_quality": {"AirQualityEntity"},
|
"air_quality": {"AirQualityEntity"},
|
||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
@ -63,6 +65,7 @@ _MODULES: dict[str, set[str]] = {
|
|||||||
"WeatherEntityDescription",
|
"WeatherEntityDescription",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
_PLATFORMS: set[str] = {platform.value for platform in Platform}
|
||||||
|
|
||||||
|
|
||||||
class HassEnforceClassModule(BaseChecker):
|
class HassEnforceClassModule(BaseChecker):
|
||||||
@ -89,6 +92,18 @@ class HassEnforceClassModule(BaseChecker):
|
|||||||
current_integration = parts[2]
|
current_integration = parts[2]
|
||||||
current_module = parts[3] if len(parts) > 3 else ""
|
current_module = parts[3] if len(parts) > 3 else ""
|
||||||
|
|
||||||
|
if current_module != "entity" and current_integration not in _PLATFORMS:
|
||||||
|
top_level_ancestors = list(node.ancestors(recurs=False))
|
||||||
|
|
||||||
|
for ancestor in top_level_ancestors:
|
||||||
|
if ancestor.name == "Entity":
|
||||||
|
self.add_message(
|
||||||
|
"hass-enforce-class-module",
|
||||||
|
node=node,
|
||||||
|
args=(ancestor.name, "entity"),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
ancestors: list[ClassDef] | None = None
|
ancestors: list[ClassDef] | None = None
|
||||||
|
|
||||||
for expected_module, classes in _MODULES.items():
|
for expected_module, classes in _MODULES.items():
|
||||||
|
@ -192,3 +192,73 @@ def test_enforce_class_module_bad_nested(
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
walker.walk(root_node)
|
walker.walk(root_node)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
[
|
||||||
|
"homeassistant.components.sensor",
|
||||||
|
"homeassistant.components.sensor.entity",
|
||||||
|
"homeassistant.components.pylint_test.entity",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_enforce_entity_good(
|
||||||
|
linter: UnittestLinter,
|
||||||
|
enforce_class_module_checker: BaseChecker,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
"""Good test cases."""
|
||||||
|
code = """
|
||||||
|
class Entity:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CustomEntity(Entity):
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
root_node = astroid.parse(code, path)
|
||||||
|
walker = ASTWalker(linter)
|
||||||
|
walker.add_checker(enforce_class_module_checker)
|
||||||
|
|
||||||
|
with assert_no_messages(linter):
|
||||||
|
walker.walk(root_node)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
[
|
||||||
|
"homeassistant.components.pylint_test",
|
||||||
|
"homeassistant.components.pylint_test.select",
|
||||||
|
"homeassistant.components.pylint_test.select.entity",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_enforce_entity_bad(
|
||||||
|
linter: UnittestLinter,
|
||||||
|
enforce_class_module_checker: BaseChecker,
|
||||||
|
path: str,
|
||||||
|
) -> None:
|
||||||
|
"""Good test cases."""
|
||||||
|
code = """
|
||||||
|
class Entity:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CustomEntity(Entity):
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
root_node = astroid.parse(code, path)
|
||||||
|
walker = ASTWalker(linter)
|
||||||
|
walker.add_checker(enforce_class_module_checker)
|
||||||
|
|
||||||
|
with assert_adds_messages(
|
||||||
|
linter,
|
||||||
|
MessageTest(
|
||||||
|
msg_id="hass-enforce-class-module",
|
||||||
|
line=5,
|
||||||
|
node=root_node.body[1],
|
||||||
|
args=("Entity", "entity"),
|
||||||
|
confidence=UNDEFINED,
|
||||||
|
col_offset=0,
|
||||||
|
end_line=5,
|
||||||
|
end_col_offset=18,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
walker.walk(root_node)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user