mirror of https://github.com/home-assistant/core
Add checks for lock properties in type-hint plugin (#73729)
* Add checks for lock properties in type-hint plugin * Adjust comment * Simplify return-type * Only check properties when ignore_missing_annotations is disabled * Adjust tests * Add comment * Adjust docstring
This commit is contained in:
parent
c674af3ba1
commit
1b8dd3368a
|
@ -20,8 +20,8 @@ class TypeHintMatch:
|
|||
"""Class for pattern matching."""
|
||||
|
||||
function_name: str
|
||||
arg_types: dict[int, str]
|
||||
return_type: list[str] | str | None | object
|
||||
arg_types: dict[int, str] | None = None
|
||||
check_return_type_inheritance: bool = False
|
||||
|
||||
|
||||
|
@ -440,7 +440,42 @@ _CLASS_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
|||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
}
|
||||
# Properties are normally checked by mypy, and will only be checked
|
||||
# by pylint when --ignore-missing-annotations is False
|
||||
_PROPERTY_MATCH: dict[str, list[ClassTypeHintMatch]] = {
|
||||
"lock": [
|
||||
ClassTypeHintMatch(
|
||||
base_class="LockEntity",
|
||||
matches=[
|
||||
TypeHintMatch(
|
||||
function_name="changed_by",
|
||||
return_type=["str", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="code_format",
|
||||
return_type=["str", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="is_locked",
|
||||
return_type=["bool", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="is_locking",
|
||||
return_type=["bool", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="is_unlocking",
|
||||
return_type=["bool", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="is_jammed",
|
||||
return_type=["bool", None],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
@ -621,7 +656,12 @@ class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
|
|||
self._function_matchers.extend(function_matches)
|
||||
|
||||
if class_matches := _CLASS_MATCH.get(module_platform):
|
||||
self._class_matchers = class_matches
|
||||
self._class_matchers.extend(class_matches)
|
||||
|
||||
if not self.linter.config.ignore_missing_annotations and (
|
||||
property_matches := _PROPERTY_MATCH.get(module_platform)
|
||||
):
|
||||
self._class_matchers.extend(property_matches)
|
||||
|
||||
def visit_classdef(self, node: nodes.ClassDef) -> None:
|
||||
"""Called when a ClassDef node is visited."""
|
||||
|
@ -659,14 +699,15 @@ class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
|
|||
):
|
||||
return
|
||||
|
||||
# Check that all arguments are correctly annotated.
|
||||
for key, expected_type in match.arg_types.items():
|
||||
if not _is_valid_type(expected_type, annotations[key]):
|
||||
self.add_message(
|
||||
"hass-argument-type",
|
||||
node=node.args.args[key],
|
||||
args=(key + 1, expected_type),
|
||||
)
|
||||
# Check that all positional arguments are correctly annotated.
|
||||
if match.arg_types:
|
||||
for key, expected_type in match.arg_types.items():
|
||||
if not _is_valid_type(expected_type, annotations[key]):
|
||||
self.add_message(
|
||||
"hass-argument-type",
|
||||
node=node.args.args[key],
|
||||
args=(key + 1, expected_type),
|
||||
)
|
||||
|
||||
# Check the return type.
|
||||
if not _is_valid_return_type(match, node.returns):
|
||||
|
|
|
@ -469,3 +469,69 @@ def test_valid_config_flow_async_get_options_flow(
|
|||
|
||||
with assert_no_messages(linter):
|
||||
type_hint_checker.visit_classdef(class_node)
|
||||
|
||||
|
||||
def test_invalid_entity_properties(
|
||||
linter: UnittestLinter, type_hint_checker: BaseChecker
|
||||
) -> None:
|
||||
"""Check missing entity properties when ignore_missing_annotations is False."""
|
||||
# Set bypass option
|
||||
type_hint_checker.config.ignore_missing_annotations = False
|
||||
|
||||
class_node, prop_node = astroid.extract_node(
|
||||
"""
|
||||
class LockEntity():
|
||||
pass
|
||||
|
||||
class DoorLock( #@
|
||||
LockEntity
|
||||
):
|
||||
@property
|
||||
def changed_by( #@
|
||||
self
|
||||
):
|
||||
pass
|
||||
""",
|
||||
"homeassistant.components.pylint_test.lock",
|
||||
)
|
||||
type_hint_checker.visit_module(class_node.parent)
|
||||
|
||||
with assert_adds_messages(
|
||||
linter,
|
||||
pylint.testutils.MessageTest(
|
||||
msg_id="hass-return-type",
|
||||
node=prop_node,
|
||||
args=["str", None],
|
||||
line=9,
|
||||
col_offset=4,
|
||||
end_line=9,
|
||||
end_col_offset=18,
|
||||
),
|
||||
):
|
||||
type_hint_checker.visit_classdef(class_node)
|
||||
|
||||
|
||||
def test_ignore_invalid_entity_properties(
|
||||
linter: UnittestLinter, type_hint_checker: BaseChecker
|
||||
) -> None:
|
||||
"""Check invalid entity properties are ignored by default."""
|
||||
class_node = astroid.extract_node(
|
||||
"""
|
||||
class LockEntity():
|
||||
pass
|
||||
|
||||
class DoorLock( #@
|
||||
LockEntity
|
||||
):
|
||||
@property
|
||||
def changed_by(
|
||||
self
|
||||
):
|
||||
pass
|
||||
""",
|
||||
"homeassistant.components.pylint_test.lock",
|
||||
)
|
||||
type_hint_checker.visit_module(class_node.parent)
|
||||
|
||||
with assert_no_messages(linter):
|
||||
type_hint_checker.visit_classdef(class_node)
|
||||
|
|
Loading…
Reference in New Issue