mirror of
https://github.com/home-assistant/core.git
synced 2026-10-06 14:29:21 -04:00
Add pylint checker for CONNECTION_CLASS in config flows (#183937)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
e49242244b
commit
6edc3771fd
@@ -1042,3 +1042,13 @@ The light provides `color_mode` but no `supported_color_modes`. At runtime
|
||||
("does not set supported color modes") from both `state_attributes` and
|
||||
`capability_attributes` whenever `supported_color_modes` is `None`. Set
|
||||
`_attr_supported_color_modes` or override the `supported_color_modes` property.
|
||||
|
||||
|
||||
## `home_assistant_enforce_config_flow_no_connection_class` checker
|
||||
|
||||
Detects config flow classes that set `CONNECTION_CLASS`.
|
||||
|
||||
### `W7438`: `home-assistant-config-flow-connection-class`
|
||||
|
||||
`CONNECTION_CLASS` is no longer used by Home Assistant and should not be set
|
||||
on config flows. Remove the attribute.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Checker for the removed CONNECTION_CLASS attribute in config flows."""
|
||||
|
||||
from astroid import nodes
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.lint import PyLinter
|
||||
|
||||
from pylint_home_assistant.const import Module
|
||||
from pylint_home_assistant.helpers.module_info import parse_module
|
||||
|
||||
_CONNECTION_CLASS = "CONNECTION_CLASS"
|
||||
|
||||
|
||||
class HassEnforceConfigFlowNoConnectionClassChecker(BaseChecker):
|
||||
"""Checker for CONNECTION_CLASS in config flows."""
|
||||
|
||||
name = "home_assistant_enforce_config_flow_no_connection_class"
|
||||
priority = -1
|
||||
msgs = {
|
||||
"W7438": (
|
||||
"Config flow should not set CONNECTION_CLASS",
|
||||
"home-assistant-config-flow-connection-class",
|
||||
"Used when a config flow class sets CONNECTION_CLASS. The attribute "
|
||||
"is no longer used by Home Assistant and should not be set.",
|
||||
),
|
||||
}
|
||||
options = ()
|
||||
|
||||
def visit_assign(self, node: nodes.Assign) -> None:
|
||||
"""Check class-level assignments."""
|
||||
if any(
|
||||
isinstance(target, nodes.AssignName) and target.name == _CONNECTION_CLASS
|
||||
for target in node.targets
|
||||
):
|
||||
self._check(node)
|
||||
|
||||
def visit_annassign(self, node: nodes.AnnAssign) -> None:
|
||||
"""Check annotated class-level assignments."""
|
||||
if (
|
||||
isinstance(node.target, nodes.AssignName)
|
||||
and node.target.name == _CONNECTION_CLASS
|
||||
):
|
||||
self._check(node)
|
||||
|
||||
def _check(self, node: nodes.Assign | nodes.AnnAssign) -> None:
|
||||
"""Add a message if the assignment is in a config flow class body."""
|
||||
parsed = parse_module(node.root().name)
|
||||
if parsed is None or parsed.module != Module.CONFIG_FLOW:
|
||||
return
|
||||
if not isinstance(node.parent, nodes.ClassDef):
|
||||
return
|
||||
self.add_message("home-assistant-config-flow-connection-class", node=node)
|
||||
|
||||
|
||||
def register(linter: PyLinter) -> None:
|
||||
"""Register the checker."""
|
||||
linter.register_checker(HassEnforceConfigFlowNoConnectionClassChecker(linter))
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Tests for the config flow no CONNECTION_CLASS checker."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import astroid
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.testutils.unittest_linter import UnittestLinter
|
||||
import pytest
|
||||
|
||||
from tests.pylint import assert_no_messages, walk_checker
|
||||
|
||||
CONFIG_FLOW_MODULE = "homeassistant.components.test.config_flow"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("code", "module_name"),
|
||||
[
|
||||
pytest.param(
|
||||
"""
|
||||
class TestFlow:
|
||||
VERSION = 1
|
||||
""",
|
||||
CONFIG_FLOW_MODULE,
|
||||
id="no_connection_class",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
class TestFlow:
|
||||
CONNECTION_CLASS = "local_polling"
|
||||
""",
|
||||
"homeassistant.components.test.sensor",
|
||||
id="other_module",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
CONNECTION_CLASS = "local_polling"
|
||||
""",
|
||||
CONFIG_FLOW_MODULE,
|
||||
id="module_level",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_no_connection_class(
|
||||
linter: UnittestLinter,
|
||||
enforce_config_flow_no_connection_class_checker: BaseChecker,
|
||||
code: str,
|
||||
module_name: str,
|
||||
) -> None:
|
||||
"""Good test cases."""
|
||||
root_node = astroid.parse(code, module_name)
|
||||
|
||||
with assert_no_messages(linter):
|
||||
walk_checker(linter, enforce_config_flow_no_connection_class_checker, root_node)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"code",
|
||||
[
|
||||
pytest.param(
|
||||
"""
|
||||
class TestFlow:
|
||||
CONNECTION_CLASS = "local_polling"
|
||||
""",
|
||||
id="assign",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
class TestFlow:
|
||||
CONNECTION_CLASS: str = "local_polling"
|
||||
""",
|
||||
id="annotated_assign",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_no_connection_class_bad(
|
||||
linter: UnittestLinter,
|
||||
enforce_config_flow_no_connection_class_checker: BaseChecker,
|
||||
code: str,
|
||||
) -> None:
|
||||
"""Bad test cases."""
|
||||
root_node = astroid.parse(code, CONFIG_FLOW_MODULE)
|
||||
|
||||
walk_checker(linter, enforce_config_flow_no_connection_class_checker, root_node)
|
||||
messages = linter.release_messages()
|
||||
assert len(messages) == 1
|
||||
assert messages[0].msg_id == "home-assistant-config-flow-connection-class"
|
||||
@@ -3,6 +3,9 @@
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.testutils.unittest_linter import UnittestLinter
|
||||
from pylint_home_assistant.checkers.class_module import HassEnforceClassModule
|
||||
from pylint_home_assistant.checkers.config_flow.no_connection_class import (
|
||||
HassEnforceConfigFlowNoConnectionClassChecker,
|
||||
)
|
||||
from pylint_home_assistant.checkers.config_flow.no_name import (
|
||||
HassEnforceConfigFlowNoNameChecker,
|
||||
)
|
||||
@@ -109,6 +112,17 @@ def enforce_serial_port_selector_usb_checker_fixture(
|
||||
return checker
|
||||
|
||||
|
||||
@pytest.fixture(name="enforce_config_flow_no_connection_class_checker")
|
||||
def enforce_config_flow_no_connection_class_checker_fixture(
|
||||
linter: UnittestLinter,
|
||||
) -> BaseChecker:
|
||||
"""Fixture to provide a config_flow_no_connection_class checker."""
|
||||
clear_caches()
|
||||
checker = HassEnforceConfigFlowNoConnectionClassChecker(linter)
|
||||
checker.module = "homeassistant.components.pylint_test"
|
||||
return checker
|
||||
|
||||
|
||||
@pytest.fixture(name="enforce_config_flow_no_name_checker")
|
||||
def enforce_config_flow_no_name_checker_fixture(
|
||||
linter: UnittestLinter,
|
||||
|
||||
Reference in New Issue
Block a user