Refactor type-hint pylint plugin (#72692)

* Cleanup unused variable

* Adjust tests

* Refactor _METHOD_MATCH dict

* Remove duplicate function

* Early exit

* Undo object hint

* METHOD > FUNCTION

* Add comment

* Remove extend

* Remove break

* Extract __any_platform__

* Add tests

* Cache _PLATFORMS

* Adjust tests

* Review comments

* mypy

* shorthand
This commit is contained in:
epenet 2022-05-31 11:20:31 +02:00 committed by GitHub
parent 5dc4c89acc
commit cf17169b0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 405 additions and 401 deletions

View File

@ -13,18 +13,19 @@ from homeassistant.const import Platform
UNDEFINED = object()
_PLATFORMS: set[str] = {platform.value for platform in Platform}
@dataclass
class TypeHintMatch:
"""Class for pattern matching."""
module_filter: re.Pattern
function_name: str
arg_types: dict[int, str]
return_type: list[str] | str | None
return_type: list[str] | str | None | object
_TYPE_HINT_MATCHERS: dict[str, re.Pattern] = {
_TYPE_HINT_MATCHERS: dict[str, re.Pattern[str]] = {
# a_or_b matches items such as "DiscoveryInfoType | None"
"a_or_b": re.compile(r"^(\w+) \| (\w+)$"),
# x_of_y matches items such as "Awaitable[None]"
@ -35,373 +36,333 @@ _TYPE_HINT_MATCHERS: dict[str, re.Pattern] = {
"x_of_y_of_z_comma_a": re.compile(r"^(\w+)\[(\w+)\[(.*?]*), (.*?]*)\]\]$"),
}
_MODULE_FILTERS: dict[str, re.Pattern] = {
# init matches only in the package root (__init__.py)
"init": re.compile(r"^homeassistant\.components\.\w+$"),
# any_platform matches any platform in the package root ({platform}.py)
"any_platform": re.compile(
f"^homeassistant\\.components\\.\\w+\\.({'|'.join([platform.value for platform in Platform])})$"
),
# application_credentials matches only in the package root (application_credentials.py)
"application_credentials": re.compile(
r"^homeassistant\.components\.\w+\.(application_credentials)$"
),
# backup matches only in the package root (backup.py)
"backup": re.compile(r"^homeassistant\.components\.\w+\.(backup)$"),
# cast matches only in the package root (cast.py)
"cast": re.compile(r"^homeassistant\.components\.\w+\.(cast)$"),
# config_flow matches only in the package root (config_flow.py)
"config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$"),
# device_action matches only in the package root (device_action.py)
"device_action": re.compile(r"^homeassistant\.components\.\w+\.(device_action)$"),
# device_condition matches only in the package root (device_condition.py)
"device_condition": re.compile(
r"^homeassistant\.components\.\w+\.(device_condition)$"
),
# device_tracker matches only in the package root (device_tracker.py)
"device_tracker": re.compile(r"^homeassistant\.components\.\w+\.(device_tracker)$"),
# device_trigger matches only in the package root (device_trigger.py)
"device_trigger": re.compile(r"^homeassistant\.components\.\w+\.(device_trigger)$"),
# diagnostics matches only in the package root (diagnostics.py)
"diagnostics": re.compile(r"^homeassistant\.components\.\w+\.(diagnostics)$"),
_MODULE_REGEX: re.Pattern[str] = re.compile(r"^homeassistant\.components\.\w+(\.\w+)?$")
_FUNCTION_MATCH: dict[str, list[TypeHintMatch]] = {
"__init__": [
TypeHintMatch(
function_name="setup",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_setup",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_remove_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type=None,
),
TypeHintMatch(
function_name="async_unload_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_migrate_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
],
"__any_platform__": [
TypeHintMatch(
function_name="setup_platform",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AddEntitiesCallback",
3: "DiscoveryInfoType | None",
},
return_type=None,
),
TypeHintMatch(
function_name="async_setup_platform",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AddEntitiesCallback",
3: "DiscoveryInfoType | None",
},
return_type=None,
),
TypeHintMatch(
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "AddEntitiesCallback",
},
return_type=None,
),
],
"application_credentials": [
TypeHintMatch(
function_name="async_get_auth_implementation",
arg_types={
0: "HomeAssistant",
1: "str",
2: "ClientCredential",
},
return_type="AbstractOAuth2Implementation",
),
TypeHintMatch(
function_name="async_get_authorization_server",
arg_types={
0: "HomeAssistant",
},
return_type="AuthorizationServer",
),
],
"backup": [
TypeHintMatch(
function_name="async_pre_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
TypeHintMatch(
function_name="async_post_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
],
"cast": [
TypeHintMatch(
function_name="async_get_media_browser_root_object",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type="list[BrowseMedia]",
),
TypeHintMatch(
function_name="async_browse_media",
arg_types={
0: "HomeAssistant",
1: "str",
2: "str",
3: "str",
},
return_type=["BrowseMedia", "BrowseMedia | None"],
),
TypeHintMatch(
function_name="async_play_media",
arg_types={
0: "HomeAssistant",
1: "str",
2: "Chromecast",
3: "str",
4: "str",
},
return_type="bool",
),
],
"config_flow": [
TypeHintMatch(
function_name="_async_has_devices",
arg_types={
0: "HomeAssistant",
},
return_type="bool",
),
],
"device_action": [
TypeHintMatch(
function_name="async_validate_action_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_call_action_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "TemplateVarsType",
3: "Context | None",
},
return_type=None,
),
TypeHintMatch(
function_name="async_get_action_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_actions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"device_condition": [
TypeHintMatch(
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_condition_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConditionCheckerType",
),
TypeHintMatch(
function_name="async_get_condition_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_conditions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"device_tracker": [
TypeHintMatch(
function_name="setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "Callable[..., None]",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
function_name="async_setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "Callable[..., Awaitable[None]]",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
function_name="get_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type=["DeviceScanner", "DeviceScanner | None"],
),
TypeHintMatch(
function_name="async_get_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type=["DeviceScanner", "DeviceScanner | None"],
),
],
"device_trigger": [
TypeHintMatch(
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
function_name="async_attach_trigger",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AutomationActionType",
3: "AutomationTriggerInfo",
},
return_type="CALLBACK_TYPE",
),
TypeHintMatch(
function_name="async_get_trigger_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
function_name="async_get_triggers",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
],
"diagnostics": [
TypeHintMatch(
function_name="async_get_config_entry_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type=UNDEFINED,
),
TypeHintMatch(
function_name="async_get_device_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "DeviceEntry",
},
return_type=UNDEFINED,
),
],
}
_METHOD_MATCH: list[TypeHintMatch] = [
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="setup",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="async_setup",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="async_remove_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="async_unload_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["init"],
function_name="async_migrate_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["any_platform"],
function_name="setup_platform",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AddEntitiesCallback",
3: "DiscoveryInfoType | None",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["any_platform"],
function_name="async_setup_platform",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AddEntitiesCallback",
3: "DiscoveryInfoType | None",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["any_platform"],
function_name="async_setup_entry",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "AddEntitiesCallback",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["application_credentials"],
function_name="async_get_auth_implementation",
arg_types={
0: "HomeAssistant",
1: "str",
2: "ClientCredential",
},
return_type="AbstractOAuth2Implementation",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["application_credentials"],
function_name="async_get_authorization_server",
arg_types={
0: "HomeAssistant",
},
return_type="AuthorizationServer",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["backup"],
function_name="async_pre_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["backup"],
function_name="async_post_backup",
arg_types={
0: "HomeAssistant",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["cast"],
function_name="async_get_media_browser_root_object",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type="list[BrowseMedia]",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["cast"],
function_name="async_browse_media",
arg_types={
0: "HomeAssistant",
1: "str",
2: "str",
3: "str",
},
return_type=["BrowseMedia", "BrowseMedia | None"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["cast"],
function_name="async_play_media",
arg_types={
0: "HomeAssistant",
1: "str",
2: "Chromecast",
3: "str",
4: "str",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["config_flow"],
function_name="_async_has_devices",
arg_types={
0: "HomeAssistant",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_action"],
function_name="async_validate_action_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_action"],
function_name="async_call_action_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "TemplateVarsType",
3: "Context | None",
},
return_type=None,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_action"],
function_name="async_get_action_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_action"],
function_name="async_get_actions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_condition"],
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_condition"],
function_name="async_condition_from_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConditionCheckerType",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_condition"],
function_name="async_get_condition_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_condition"],
function_name="async_get_conditions",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_tracker"],
function_name="setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "Callable[..., None]",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_tracker"],
function_name="async_setup_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "Callable[..., Awaitable[None]]",
3: "DiscoveryInfoType | None",
},
return_type="bool",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_tracker"],
function_name="get_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type=["DeviceScanner", "DeviceScanner | None"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_tracker"],
function_name="async_get_scanner",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type=["DeviceScanner", "DeviceScanner | None"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_trigger"],
function_name="async_validate_condition_config",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="ConfigType",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_trigger"],
function_name="async_attach_trigger",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
2: "AutomationActionType",
3: "AutomationTriggerInfo",
},
return_type="CALLBACK_TYPE",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_trigger"],
function_name="async_get_trigger_capabilities",
arg_types={
0: "HomeAssistant",
1: "ConfigType",
},
return_type="dict[str, Schema]",
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["device_trigger"],
function_name="async_get_triggers",
arg_types={
0: "HomeAssistant",
1: "str",
},
return_type=["list[dict[str, str]]", "list[dict[str, Any]]"],
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["diagnostics"],
function_name="async_get_config_entry_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
},
return_type=UNDEFINED,
),
TypeHintMatch(
module_filter=_MODULE_FILTERS["diagnostics"],
function_name="async_get_device_diagnostics",
arg_types={
0: "HomeAssistant",
1: "ConfigEntry",
2: "DeviceEntry",
},
return_type=UNDEFINED,
),
]
def _is_valid_type(expected_type: list[str] | str | None, node: astroid.NodeNG) -> bool:
def _is_valid_type(
expected_type: list[str] | str | None | object, node: astroid.NodeNG
) -> bool:
"""Check the argument node against the expected type."""
if expected_type is UNDEFINED:
return True
@ -416,6 +377,8 @@ def _is_valid_type(expected_type: list[str] | str | None, node: astroid.NodeNG)
if expected_type is None or expected_type == "None":
return isinstance(node, astroid.Const) and node.value is None
assert isinstance(expected_type, str)
# Const occurs when the type is an Ellipsis
if expected_type == "...":
return isinstance(node, astroid.Const) and node.value == Ellipsis
@ -487,6 +450,17 @@ def _has_valid_annotations(
return False
def _get_module_platform(module_name: str) -> str | None:
"""Called when a Module node is visited."""
if not (module_match := _MODULE_REGEX.match(module_name)):
# Ensure `homeassistant.components.<component>`
# Or `homeassistant.components.<component>.<platform>`
return None
platform = module_match.groups()[0]
return platform.lstrip(".") if platform else "__init__"
class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
"""Checker for setup type hints."""
@ -510,38 +484,31 @@ class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
def __init__(self, linter: PyLinter | None = None) -> None:
super().__init__(linter)
self.current_package: str | None = None
self.module: str | None = None
self._function_matchers: list[TypeHintMatch] = []
def visit_module(self, node: astroid.Module) -> None:
"""Called when a Module node is visited."""
self.module = node.name
if node.package:
self.current_package = node.name
else:
# Strip name of the current module
self.current_package = node.name[: node.name.rfind(".")]
self._function_matchers = []
if (module_platform := _get_module_platform(node.name)) is None:
return
if module_platform in _PLATFORMS:
self._function_matchers.extend(_FUNCTION_MATCH["__any_platform__"])
if matches := _FUNCTION_MATCH.get(module_platform):
self._function_matchers.extend(matches)
def visit_functiondef(self, node: astroid.FunctionDef) -> None:
"""Called when a FunctionDef node is visited."""
for match in _METHOD_MATCH:
self._visit_functiondef(node, match)
for match in self._function_matchers:
if node.name != match.function_name or node.is_method():
continue
self._check_function(node, match)
def visit_asyncfunctiondef(self, node: astroid.AsyncFunctionDef) -> None:
"""Called when an AsyncFunctionDef node is visited."""
for match in _METHOD_MATCH:
self._visit_functiondef(node, match)
def _visit_functiondef(
self, node: astroid.FunctionDef, match: TypeHintMatch
) -> None:
if node.name != match.function_name:
return
if node.is_method():
return
if not match.module_filter.match(self.module):
return
visit_asyncfunctiondef = visit_functiondef
def _check_function(self, node: astroid.FunctionDef, match: TypeHintMatch) -> None:
# Check that at least one argument is annotated.
annotations = _get_all_annotations(node)
if node.returns is None and not _has_valid_annotations(annotations):

View File

@ -1,5 +1,6 @@
"""Tests for pylint hass_enforce_type_hints plugin."""
# pylint:disable=protected-access
from __future__ import annotations
import re
from types import ModuleType
@ -14,6 +15,30 @@ import pytest
from . import assert_adds_messages, assert_no_messages
@pytest.mark.parametrize(
("module_name", "expected_platform", "in_platforms"),
[
("homeassistant", None, False),
("homeassistant.components", None, False),
("homeassistant.components.pylint_test", "__init__", False),
("homeassistant.components.pylint_test.config_flow", "config_flow", False),
("homeassistant.components.pylint_test.light", "light", True),
("homeassistant.components.pylint_test.light.v1", None, False),
],
)
def test_regex_get_module_platform(
hass_enforce_type_hints: ModuleType,
module_name: str,
expected_platform: str | None,
in_platforms: bool,
) -> None:
"""Test _get_module_platform regex."""
platform = hass_enforce_type_hints._get_module_platform(module_name)
assert platform == expected_platform
assert (platform in hass_enforce_type_hints._PLATFORMS) == in_platforms
@pytest.mark.parametrize(
("string", "expected_x", "expected_y", "expected_z", "expected_a"),
[
@ -95,7 +120,11 @@ def test_ignore_not_annotations(
hass_enforce_type_hints: ModuleType, type_hint_checker: BaseChecker, code: str
) -> None:
"""Ensure that _is_valid_type is not run if there are no annotations."""
func_node = astroid.extract_node(code)
func_node = astroid.extract_node(
code,
"homeassistant.components.pylint_test",
)
type_hint_checker.visit_module(func_node.parent)
with patch.object(
hass_enforce_type_hints, "_is_valid_type", return_value=True
@ -131,7 +160,11 @@ def test_dont_ignore_partial_annotations(
hass_enforce_type_hints: ModuleType, type_hint_checker: BaseChecker, code: str
) -> None:
"""Ensure that _is_valid_type is run if there is at least one annotation."""
func_node = astroid.extract_node(code)
func_node = astroid.extract_node(
code,
"homeassistant.components.pylint_test",
)
type_hint_checker.visit_module(func_node.parent)
with patch.object(
hass_enforce_type_hints, "_is_valid_type", return_value=True
@ -144,7 +177,6 @@ def test_invalid_discovery_info(
linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
"""Ensure invalid hints are rejected for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_tracker"
func_node, discovery_info_node = astroid.extract_node(
"""
async def async_setup_scanner( #@
@ -154,8 +186,10 @@ def test_invalid_discovery_info(
discovery_info: dict[str, Any] | None = None, #@
) -> bool:
pass
"""
""",
"homeassistant.components.pylint_test.device_tracker",
)
type_hint_checker.visit_module(func_node.parent)
with assert_adds_messages(
linter,
@ -176,7 +210,6 @@ def test_valid_discovery_info(
linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
"""Ensure valid hints are accepted for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_tracker"
func_node = astroid.extract_node(
"""
async def async_setup_scanner( #@
@ -186,8 +219,10 @@ def test_valid_discovery_info(
discovery_info: DiscoveryInfoType | None = None,
) -> bool:
pass
"""
""",
"homeassistant.components.pylint_test.device_tracker",
)
type_hint_checker.visit_module(func_node.parent)
with assert_no_messages(linter):
type_hint_checker.visit_asyncfunctiondef(func_node)
@ -197,7 +232,6 @@ def test_invalid_list_dict_str_any(
linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
"""Ensure invalid hints are rejected for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger"
func_node = astroid.extract_node(
"""
async def async_get_triggers( #@
@ -205,8 +239,10 @@ def test_invalid_list_dict_str_any(
device_id: str
) -> list:
pass
"""
""",
"homeassistant.components.pylint_test.device_trigger",
)
type_hint_checker.visit_module(func_node.parent)
with assert_adds_messages(
linter,
@ -227,7 +263,6 @@ def test_valid_list_dict_str_any(
linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
"""Ensure valid hints are accepted for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger"
func_node = astroid.extract_node(
"""
async def async_get_triggers( #@
@ -235,8 +270,10 @@ def test_valid_list_dict_str_any(
device_id: str
) -> list[dict[str, Any]]:
pass
"""
""",
"homeassistant.components.pylint_test.device_trigger",
)
type_hint_checker.visit_module(func_node.parent)
with assert_no_messages(linter):
type_hint_checker.visit_asyncfunctiondef(func_node)