mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Add pylint plugin to check for calls to base implementation (#100432)
This commit is contained in:
parent
ddd62a8f63
commit
37288d7788
@ -421,6 +421,7 @@ class AirVisualEntity(CoordinatorEntity):
|
|||||||
self._entry = entry
|
self._entry = entry
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ class FloSwitch(FloEntity, SwitchEntity):
|
|||||||
self._attr_is_on = self._device.last_known_valve_state == "open"
|
self._attr_is_on = self._device.last_known_valve_state == "open"
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self.async_on_remove(self._device.async_add_listener(self.async_update_state))
|
self.async_on_remove(self._device.async_add_listener(self.async_update_state))
|
||||||
|
@ -311,6 +311,7 @@ class PowerViewShadeBase(ShadeEntity, CoverEntity):
|
|||||||
await self.async_update()
|
await self.async_update()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
|
@ -136,6 +136,7 @@ class PowerViewSensor(ShadeEntity, SensorEntity):
|
|||||||
"""Get the current value in percentage."""
|
"""Get the current value in percentage."""
|
||||||
return self.entity_description.native_value_fn(self._shade)
|
return self.entity_description.native_value_fn(self._shade)
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
|
@ -192,6 +192,7 @@ class HvvDepartureBinarySensor(CoordinatorEntity, BinarySensorEntity):
|
|||||||
if v is not None
|
if v is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
|
@ -262,6 +262,7 @@ class ISYAuxSensorEntity(ISYSensorEntity):
|
|||||||
"""Return the target value."""
|
"""Return the target value."""
|
||||||
return None if self.target is None else self.target.value
|
return None if self.target is None else self.target.value
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Subscribe to the node control change events.
|
"""Subscribe to the node control change events.
|
||||||
|
|
||||||
|
@ -156,6 +156,7 @@ class ISYEnableSwitchEntity(ISYAuxControlEntity, SwitchEntity):
|
|||||||
self._attr_name = description.name # Override super
|
self._attr_name = description.name # Override super
|
||||||
self._change_handler: EventListener = None
|
self._change_handler: EventListener = None
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Subscribe to the node control change events."""
|
"""Subscribe to the node control change events."""
|
||||||
self._change_handler = self._node.isy.nodes.status_events.subscribe(
|
self._change_handler = self._node.isy.nodes.status_events.subscribe(
|
||||||
|
@ -64,6 +64,7 @@ class LivisiEntity(CoordinatorEntity[LivisiDataUpdateCoordinator]):
|
|||||||
)
|
)
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callback for reachability."""
|
"""Register callback for reachability."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
|
@ -63,6 +63,7 @@ class LutronOccupancySensor(LutronCasetaDevice, BinarySensorEntity):
|
|||||||
"""Return the brightness of the light."""
|
"""Return the brightness of the light."""
|
||||||
return self._device["status"] == OCCUPANCY_GROUP_OCCUPIED
|
return self._device["status"] == OCCUPANCY_GROUP_OCCUPIED
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
self._smartbridge.add_occupancy_subscriber(
|
self._smartbridge.add_occupancy_subscriber(
|
||||||
|
@ -352,6 +352,7 @@ class RflinkSensor(RflinkDevice, SensorEntity):
|
|||||||
"""Domain specific event handler."""
|
"""Domain specific event handler."""
|
||||||
self._state = event["value"]
|
self._state = event["value"]
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register update callback."""
|
"""Register update callback."""
|
||||||
# Remove temporary bogus entity_id if added
|
# Remove temporary bogus entity_id if added
|
||||||
|
@ -35,6 +35,7 @@ class RiscoCloudEntity(CoordinatorEntity[RiscoDataUpdateCoordinator]):
|
|||||||
self._get_data_from_coordinator()
|
self._get_data_from_coordinator()
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
|
@ -86,6 +86,7 @@ class RiscoSensor(CoordinatorEntity[RiscoEventsDataUpdateCoordinator], SensorEnt
|
|||||||
self._attr_name = f"Risco {self.coordinator.risco.site_name} {name} Events"
|
self._attr_name = f"Risco {self.coordinator.risco.site_name} {name} Events"
|
||||||
self._attr_device_class = SensorDeviceClass.TIMESTAMP
|
self._attr_device_class = SensorDeviceClass.TIMESTAMP
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
self._entity_registry = er.async_get(self.hass)
|
self._entity_registry = er.async_get(self.hass)
|
||||||
|
@ -332,6 +332,7 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
|||||||
)
|
)
|
||||||
self._attr_unique_id = f"{coordinator.mac}-{block.description}"
|
self._attr_unique_id = f"{coordinator.mac}-{block.description}"
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to HASS."""
|
"""When entity is added to HASS."""
|
||||||
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
||||||
@ -375,6 +376,7 @@ class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]):
|
|||||||
"""Device status by entity key."""
|
"""Device status by entity key."""
|
||||||
return cast(dict, self.coordinator.device.status[self.key])
|
return cast(dict, self.coordinator.device.status[self.key])
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to HASS."""
|
"""When entity is added to HASS."""
|
||||||
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
||||||
|
@ -73,6 +73,7 @@ class SmartMeterTexasSensor(CoordinatorEntity, RestoreEntity, SensorEntity):
|
|||||||
self._attr_native_value = self.meter.reading
|
self._attr_native_value = self.meter.reading
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to updates."""
|
"""Subscribe to updates."""
|
||||||
self.async_on_remove(self.coordinator.async_add_listener(self._state_update))
|
self.async_on_remove(self.coordinator.async_add_listener(self._state_update))
|
||||||
|
@ -99,6 +99,7 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity):
|
|||||||
self._attr_available = True
|
self._attr_available = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
# pylint: disable-next=hass-missing-super-call
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Handle entity which will be added."""
|
"""Handle entity which will be added."""
|
||||||
if not self._client.subscribed:
|
if not self._client.subscribed:
|
||||||
|
79
pylint/plugins/hass_enforce_super_call.py
Normal file
79
pylint/plugins/hass_enforce_super_call.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""Plugin for checking super calls."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from astroid import nodes
|
||||||
|
from pylint.checkers import BaseChecker
|
||||||
|
from pylint.interfaces import INFERENCE
|
||||||
|
from pylint.lint import PyLinter
|
||||||
|
|
||||||
|
METHODS = {
|
||||||
|
"async_added_to_hass",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HassEnforceSuperCallChecker(BaseChecker): # type: ignore[misc]
|
||||||
|
"""Checker for super calls."""
|
||||||
|
|
||||||
|
name = "hass_enforce_super_call"
|
||||||
|
priority = -1
|
||||||
|
msgs = {
|
||||||
|
"W7441": (
|
||||||
|
"Missing call to: super().%s",
|
||||||
|
"hass-missing-super-call",
|
||||||
|
"Used when method should call its parent implementation.",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
options = ()
|
||||||
|
|
||||||
|
def visit_functiondef(
|
||||||
|
self, node: nodes.FunctionDef | nodes.AsyncFunctionDef
|
||||||
|
) -> None:
|
||||||
|
"""Check for super calls in method body."""
|
||||||
|
if node.name not in METHODS:
|
||||||
|
return
|
||||||
|
|
||||||
|
assert node.parent
|
||||||
|
parent = node.parent.frame()
|
||||||
|
if not isinstance(parent, nodes.ClassDef):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check function body for super call
|
||||||
|
for child_node in node.body:
|
||||||
|
while isinstance(child_node, (nodes.Expr, nodes.Await, nodes.Return)):
|
||||||
|
child_node = child_node.value
|
||||||
|
match child_node:
|
||||||
|
case nodes.Call(
|
||||||
|
func=nodes.Attribute(
|
||||||
|
expr=nodes.Call(func=nodes.Name(name="super")),
|
||||||
|
attrname=node.name,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for non-empty base implementation
|
||||||
|
found_base_implementation = False
|
||||||
|
for base in parent.ancestors():
|
||||||
|
for method in base.mymethods():
|
||||||
|
if method.name != node.name:
|
||||||
|
continue
|
||||||
|
if method.body and not (
|
||||||
|
len(method.body) == 1 and isinstance(method.body[0], nodes.Pass)
|
||||||
|
):
|
||||||
|
found_base_implementation = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_base_implementation:
|
||||||
|
self.add_message(
|
||||||
|
"hass-missing-super-call",
|
||||||
|
node=node,
|
||||||
|
args=(node.name,),
|
||||||
|
confidence=INFERENCE,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
visit_asyncfunctiondef = visit_functiondef
|
||||||
|
|
||||||
|
|
||||||
|
def register(linter: PyLinter) -> None:
|
||||||
|
"""Register the checker."""
|
||||||
|
linter.register_checker(HassEnforceSuperCallChecker(linter))
|
@ -100,6 +100,7 @@ init-hook = """\
|
|||||||
load-plugins = [
|
load-plugins = [
|
||||||
"pylint.extensions.code_style",
|
"pylint.extensions.code_style",
|
||||||
"pylint.extensions.typing",
|
"pylint.extensions.typing",
|
||||||
|
"hass_enforce_super_call",
|
||||||
"hass_enforce_type_hints",
|
"hass_enforce_type_hints",
|
||||||
"hass_inheritance",
|
"hass_inheritance",
|
||||||
"hass_imports",
|
"hass_imports",
|
||||||
|
@ -11,13 +11,11 @@ import pytest
|
|||||||
BASE_PATH = Path(__file__).parents[2]
|
BASE_PATH = Path(__file__).parents[2]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="hass_enforce_type_hints", scope="session")
|
def _load_plugin_from_file(module_name: str, file: str) -> ModuleType:
|
||||||
def hass_enforce_type_hints_fixture() -> ModuleType:
|
"""Load plugin from file path."""
|
||||||
"""Fixture to provide a requests mocker."""
|
|
||||||
module_name = "hass_enforce_type_hints"
|
|
||||||
spec = spec_from_file_location(
|
spec = spec_from_file_location(
|
||||||
module_name,
|
module_name,
|
||||||
str(BASE_PATH.joinpath("pylint/plugins/hass_enforce_type_hints.py")),
|
str(BASE_PATH.joinpath(file)),
|
||||||
)
|
)
|
||||||
assert spec and spec.loader
|
assert spec and spec.loader
|
||||||
|
|
||||||
@ -27,6 +25,15 @@ def hass_enforce_type_hints_fixture() -> ModuleType:
|
|||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="hass_enforce_type_hints", scope="session")
|
||||||
|
def hass_enforce_type_hints_fixture() -> ModuleType:
|
||||||
|
"""Fixture to provide a requests mocker."""
|
||||||
|
return _load_plugin_from_file(
|
||||||
|
"hass_enforce_type_hints",
|
||||||
|
"pylint/plugins/hass_enforce_type_hints.py",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="linter")
|
@pytest.fixture(name="linter")
|
||||||
def linter_fixture() -> UnittestLinter:
|
def linter_fixture() -> UnittestLinter:
|
||||||
"""Fixture to provide a requests mocker."""
|
"""Fixture to provide a requests mocker."""
|
||||||
@ -44,16 +51,10 @@ def type_hint_checker_fixture(hass_enforce_type_hints, linter) -> BaseChecker:
|
|||||||
@pytest.fixture(name="hass_imports", scope="session")
|
@pytest.fixture(name="hass_imports", scope="session")
|
||||||
def hass_imports_fixture() -> ModuleType:
|
def hass_imports_fixture() -> ModuleType:
|
||||||
"""Fixture to provide a requests mocker."""
|
"""Fixture to provide a requests mocker."""
|
||||||
module_name = "hass_imports"
|
return _load_plugin_from_file(
|
||||||
spec = spec_from_file_location(
|
"hass_imports",
|
||||||
module_name, str(BASE_PATH.joinpath("pylint/plugins/hass_imports.py"))
|
"pylint/plugins/hass_imports.py",
|
||||||
)
|
)
|
||||||
assert spec and spec.loader
|
|
||||||
|
|
||||||
module = module_from_spec(spec)
|
|
||||||
sys.modules[module_name] = module
|
|
||||||
spec.loader.exec_module(module)
|
|
||||||
return module
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="imports_checker")
|
@pytest.fixture(name="imports_checker")
|
||||||
@ -62,3 +63,20 @@ def imports_checker_fixture(hass_imports, linter) -> BaseChecker:
|
|||||||
type_hint_checker = hass_imports.HassImportsFormatChecker(linter)
|
type_hint_checker = hass_imports.HassImportsFormatChecker(linter)
|
||||||
type_hint_checker.module = "homeassistant.components.pylint_test"
|
type_hint_checker.module = "homeassistant.components.pylint_test"
|
||||||
return type_hint_checker
|
return type_hint_checker
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="hass_enforce_super_call", scope="session")
|
||||||
|
def hass_enforce_super_call_fixture() -> ModuleType:
|
||||||
|
"""Fixture to provide a requests mocker."""
|
||||||
|
return _load_plugin_from_file(
|
||||||
|
"hass_enforce_super_call",
|
||||||
|
"pylint/plugins/hass_enforce_super_call.py",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="super_call_checker")
|
||||||
|
def super_call_checker_fixture(hass_enforce_super_call, linter) -> BaseChecker:
|
||||||
|
"""Fixture to provide a requests mocker."""
|
||||||
|
super_call_checker = hass_enforce_super_call.HassEnforceSuperCallChecker(linter)
|
||||||
|
super_call_checker.module = "homeassistant.components.pylint_test"
|
||||||
|
return super_call_checker
|
||||||
|
221
tests/pylint/test_enforce_super_call.py
Normal file
221
tests/pylint/test_enforce_super_call.py
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
"""Tests for pylint hass_enforce_super_call plugin."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import ModuleType
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import astroid
|
||||||
|
from pylint.checkers import BaseChecker
|
||||||
|
from pylint.interfaces import INFERENCE
|
||||||
|
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 Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
pass
|
||||||
|
""",
|
||||||
|
id="no_parent",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
\"\"\"Some docstring.\"\"\"
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
""",
|
||||||
|
id="empty_parent_implementation",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
\"\"\"Some docstring.\"\"\"
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
""",
|
||||||
|
id="empty_parent_implementation2",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
""",
|
||||||
|
id="correct_super_call",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
return await super().async_added_to_hass()
|
||||||
|
""",
|
||||||
|
id="super_call_in_return",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
def added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
def added_to_hass(self) -> None:
|
||||||
|
super().added_to_hass()
|
||||||
|
""",
|
||||||
|
id="super_call_not_async",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
\"\"\"\"\"\"
|
||||||
|
|
||||||
|
class Coordinator:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity, Coordinator):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
""",
|
||||||
|
id="multiple_inheritance",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
async def async_added_to_hass() -> None:
|
||||||
|
x = 2
|
||||||
|
""",
|
||||||
|
id="not_a_method",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_enforce_super_call(
|
||||||
|
linter: UnittestLinter,
|
||||||
|
hass_enforce_super_call: ModuleType,
|
||||||
|
super_call_checker: BaseChecker,
|
||||||
|
code: str,
|
||||||
|
) -> None:
|
||||||
|
"""Good test cases."""
|
||||||
|
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
|
||||||
|
walker = ASTWalker(linter)
|
||||||
|
walker.add_checker(super_call_checker)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
hass_enforce_super_call, "METHODS", new={"added_to_hass", "async_added_to_hass"}
|
||||||
|
), assert_no_messages(linter):
|
||||||
|
walker.walk(root_node)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("code", "node_idx"),
|
||||||
|
[
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
def added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
def added_to_hass(self) -> None:
|
||||||
|
x = 3
|
||||||
|
""",
|
||||||
|
1,
|
||||||
|
id="no_super_call",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 3
|
||||||
|
""",
|
||||||
|
1,
|
||||||
|
id="no_super_call_async",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
await Entity.async_added_to_hass()
|
||||||
|
""",
|
||||||
|
1,
|
||||||
|
id="explicit_call_to_base_implementation",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
"""
|
||||||
|
class Entity:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
\"\"\"\"\"\"
|
||||||
|
|
||||||
|
class Coordinator:
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 2
|
||||||
|
|
||||||
|
class Child(Entity, Coordinator):
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
x = 3
|
||||||
|
""",
|
||||||
|
2,
|
||||||
|
id="multiple_inheritance",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_enforce_super_call_bad(
|
||||||
|
linter: UnittestLinter,
|
||||||
|
hass_enforce_super_call: ModuleType,
|
||||||
|
super_call_checker: BaseChecker,
|
||||||
|
code: str,
|
||||||
|
node_idx: int,
|
||||||
|
) -> None:
|
||||||
|
"""Bad test cases."""
|
||||||
|
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
|
||||||
|
walker = ASTWalker(linter)
|
||||||
|
walker.add_checker(super_call_checker)
|
||||||
|
node = root_node.body[node_idx].body[0]
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
hass_enforce_super_call, "METHODS", new={"added_to_hass", "async_added_to_hass"}
|
||||||
|
), assert_adds_messages(
|
||||||
|
linter,
|
||||||
|
MessageTest(
|
||||||
|
msg_id="hass-missing-super-call",
|
||||||
|
node=node,
|
||||||
|
line=node.lineno,
|
||||||
|
args=(node.name,),
|
||||||
|
col_offset=node.col_offset,
|
||||||
|
end_line=node.position.end_lineno,
|
||||||
|
end_col_offset=node.position.end_col_offset,
|
||||||
|
confidence=INFERENCE,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
walker.walk(root_node)
|
Loading…
x
Reference in New Issue
Block a user