mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
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."""
|
"""Class for pattern matching."""
|
||||||
|
|
||||||
function_name: str
|
function_name: str
|
||||||
arg_types: dict[int, str]
|
|
||||||
return_type: list[str] | str | None | object
|
return_type: list[str] | str | None | object
|
||||||
|
arg_types: dict[int, str] | None = None
|
||||||
check_return_type_inheritance: bool = False
|
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)
|
self._function_matchers.extend(function_matches)
|
||||||
|
|
||||||
if class_matches := _CLASS_MATCH.get(module_platform):
|
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:
|
def visit_classdef(self, node: nodes.ClassDef) -> None:
|
||||||
"""Called when a ClassDef node is visited."""
|
"""Called when a ClassDef node is visited."""
|
||||||
@ -659,14 +699,15 @@ class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check that all arguments are correctly annotated.
|
# Check that all positional arguments are correctly annotated.
|
||||||
for key, expected_type in match.arg_types.items():
|
if match.arg_types:
|
||||||
if not _is_valid_type(expected_type, annotations[key]):
|
for key, expected_type in match.arg_types.items():
|
||||||
self.add_message(
|
if not _is_valid_type(expected_type, annotations[key]):
|
||||||
"hass-argument-type",
|
self.add_message(
|
||||||
node=node.args.args[key],
|
"hass-argument-type",
|
||||||
args=(key + 1, expected_type),
|
node=node.args.args[key],
|
||||||
)
|
args=(key + 1, expected_type),
|
||||||
|
)
|
||||||
|
|
||||||
# Check the return type.
|
# Check the return type.
|
||||||
if not _is_valid_return_type(match, node.returns):
|
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):
|
with assert_no_messages(linter):
|
||||||
type_hint_checker.visit_classdef(class_node)
|
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…
x
Reference in New Issue
Block a user