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() UNDEFINED = object()
_PLATFORMS: set[str] = {platform.value for platform in Platform}
@dataclass @dataclass
class TypeHintMatch: class TypeHintMatch:
"""Class for pattern matching.""" """Class for pattern matching."""
module_filter: re.Pattern
function_name: str function_name: str
arg_types: dict[int, 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 matches items such as "DiscoveryInfoType | None"
"a_or_b": re.compile(r"^(\w+) \| (\w+)$"), "a_or_b": re.compile(r"^(\w+) \| (\w+)$"),
# x_of_y matches items such as "Awaitable[None]" # 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+)\[(.*?]*), (.*?]*)\]\]$"), "x_of_y_of_z_comma_a": re.compile(r"^(\w+)\[(\w+)\[(.*?]*), (.*?]*)\]\]$"),
} }
_MODULE_FILTERS: dict[str, re.Pattern] = { _MODULE_REGEX: re.Pattern[str] = re.compile(r"^homeassistant\.components\.\w+(\.\w+)?$")
# init matches only in the package root (__init__.py)
"init": re.compile(r"^homeassistant\.components\.\w+$"), _FUNCTION_MATCH: dict[str, list[TypeHintMatch]] = {
# any_platform matches any platform in the package root ({platform}.py) "__init__": [
"any_platform": re.compile( TypeHintMatch(
f"^homeassistant\\.components\\.\\w+\\.({'|'.join([platform.value for platform in Platform])})$" function_name="setup",
), arg_types={
# application_credentials matches only in the package root (application_credentials.py) 0: "HomeAssistant",
"application_credentials": re.compile( 1: "ConfigType",
r"^homeassistant\.components\.\w+\.(application_credentials)$" },
), return_type="bool",
# backup matches only in the package root (backup.py) ),
"backup": re.compile(r"^homeassistant\.components\.\w+\.(backup)$"), TypeHintMatch(
# cast matches only in the package root (cast.py) function_name="async_setup",
"cast": re.compile(r"^homeassistant\.components\.\w+\.(cast)$"), arg_types={
# config_flow matches only in the package root (config_flow.py) 0: "HomeAssistant",
"config_flow": re.compile(r"^homeassistant\.components\.\w+\.(config_flow)$"), 1: "ConfigType",
# device_action matches only in the package root (device_action.py) },
"device_action": re.compile(r"^homeassistant\.components\.\w+\.(device_action)$"), return_type="bool",
# device_condition matches only in the package root (device_condition.py) ),
"device_condition": re.compile( TypeHintMatch(
r"^homeassistant\.components\.\w+\.(device_condition)$" function_name="async_setup_entry",
), arg_types={
# device_tracker matches only in the package root (device_tracker.py) 0: "HomeAssistant",
"device_tracker": re.compile(r"^homeassistant\.components\.\w+\.(device_tracker)$"), 1: "ConfigEntry",
# device_trigger matches only in the package root (device_trigger.py) },
"device_trigger": re.compile(r"^homeassistant\.components\.\w+\.(device_trigger)$"), return_type="bool",
# diagnostics matches only in the package root (diagnostics.py) ),
"diagnostics": re.compile(r"^homeassistant\.components\.\w+\.(diagnostics)$"), 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(
def _is_valid_type(expected_type: list[str] | str | None, node: astroid.NodeNG) -> bool: expected_type: list[str] | str | None | object, node: astroid.NodeNG
) -> bool:
"""Check the argument node against the expected type.""" """Check the argument node against the expected type."""
if expected_type is UNDEFINED: if expected_type is UNDEFINED:
return True 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": if expected_type is None or expected_type == "None":
return isinstance(node, astroid.Const) and node.value is None return isinstance(node, astroid.Const) and node.value is None
assert isinstance(expected_type, str)
# Const occurs when the type is an Ellipsis # Const occurs when the type is an Ellipsis
if expected_type == "...": if expected_type == "...":
return isinstance(node, astroid.Const) and node.value == Ellipsis return isinstance(node, astroid.Const) and node.value == Ellipsis
@ -487,6 +450,17 @@ def _has_valid_annotations(
return False 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] class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
"""Checker for setup type hints.""" """Checker for setup type hints."""
@ -510,38 +484,31 @@ class HassTypeHintChecker(BaseChecker): # type: ignore[misc]
def __init__(self, linter: PyLinter | None = None) -> None: def __init__(self, linter: PyLinter | None = None) -> None:
super().__init__(linter) super().__init__(linter)
self.current_package: str | None = None self._function_matchers: list[TypeHintMatch] = []
self.module: str | None = None
def visit_module(self, node: astroid.Module) -> None: def visit_module(self, node: astroid.Module) -> None:
"""Called when a Module node is visited.""" """Called when a Module node is visited."""
self.module = node.name self._function_matchers = []
if node.package:
self.current_package = node.name if (module_platform := _get_module_platform(node.name)) is None:
else: return
# Strip name of the current module
self.current_package = node.name[: node.name.rfind(".")] 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: def visit_functiondef(self, node: astroid.FunctionDef) -> None:
"""Called when a FunctionDef node is visited.""" """Called when a FunctionDef node is visited."""
for match in _METHOD_MATCH: for match in self._function_matchers:
self._visit_functiondef(node, match) if node.name != match.function_name or node.is_method():
continue
self._check_function(node, match)
def visit_asyncfunctiondef(self, node: astroid.AsyncFunctionDef) -> None: visit_asyncfunctiondef = visit_functiondef
"""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
def _check_function(self, node: astroid.FunctionDef, match: TypeHintMatch) -> None:
# Check that at least one argument is annotated. # Check that at least one argument is annotated.
annotations = _get_all_annotations(node) annotations = _get_all_annotations(node)
if node.returns is None and not _has_valid_annotations(annotations): 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.""" """Tests for pylint hass_enforce_type_hints plugin."""
# pylint:disable=protected-access # pylint:disable=protected-access
from __future__ import annotations
import re import re
from types import ModuleType from types import ModuleType
@ -14,6 +15,30 @@ import pytest
from . import assert_adds_messages, assert_no_messages 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( @pytest.mark.parametrize(
("string", "expected_x", "expected_y", "expected_z", "expected_a"), ("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 hass_enforce_type_hints: ModuleType, type_hint_checker: BaseChecker, code: str
) -> None: ) -> None:
"""Ensure that _is_valid_type is not run if there are no annotations.""" """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( with patch.object(
hass_enforce_type_hints, "_is_valid_type", return_value=True 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 hass_enforce_type_hints: ModuleType, type_hint_checker: BaseChecker, code: str
) -> None: ) -> None:
"""Ensure that _is_valid_type is run if there is at least one annotation.""" """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( with patch.object(
hass_enforce_type_hints, "_is_valid_type", return_value=True 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 linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None: ) -> None:
"""Ensure invalid hints are rejected for discovery_info.""" """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( func_node, discovery_info_node = astroid.extract_node(
""" """
async def async_setup_scanner( #@ async def async_setup_scanner( #@
@ -154,8 +186,10 @@ def test_invalid_discovery_info(
discovery_info: dict[str, Any] | None = None, #@ discovery_info: dict[str, Any] | None = None, #@
) -> bool: ) -> bool:
pass pass
""" """,
"homeassistant.components.pylint_test.device_tracker",
) )
type_hint_checker.visit_module(func_node.parent)
with assert_adds_messages( with assert_adds_messages(
linter, linter,
@ -176,7 +210,6 @@ def test_valid_discovery_info(
linter: UnittestLinter, type_hint_checker: BaseChecker linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None: ) -> None:
"""Ensure valid hints are accepted for discovery_info.""" """Ensure valid hints are accepted for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_tracker"
func_node = astroid.extract_node( func_node = astroid.extract_node(
""" """
async def async_setup_scanner( #@ async def async_setup_scanner( #@
@ -186,8 +219,10 @@ def test_valid_discovery_info(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> bool: ) -> bool:
pass pass
""" """,
"homeassistant.components.pylint_test.device_tracker",
) )
type_hint_checker.visit_module(func_node.parent)
with assert_no_messages(linter): with assert_no_messages(linter):
type_hint_checker.visit_asyncfunctiondef(func_node) type_hint_checker.visit_asyncfunctiondef(func_node)
@ -197,7 +232,6 @@ def test_invalid_list_dict_str_any(
linter: UnittestLinter, type_hint_checker: BaseChecker linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None: ) -> None:
"""Ensure invalid hints are rejected for discovery_info.""" """Ensure invalid hints are rejected for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger"
func_node = astroid.extract_node( func_node = astroid.extract_node(
""" """
async def async_get_triggers( #@ async def async_get_triggers( #@
@ -205,8 +239,10 @@ def test_invalid_list_dict_str_any(
device_id: str device_id: str
) -> list: ) -> list:
pass pass
""" """,
"homeassistant.components.pylint_test.device_trigger",
) )
type_hint_checker.visit_module(func_node.parent)
with assert_adds_messages( with assert_adds_messages(
linter, linter,
@ -227,7 +263,6 @@ def test_valid_list_dict_str_any(
linter: UnittestLinter, type_hint_checker: BaseChecker linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None: ) -> None:
"""Ensure valid hints are accepted for discovery_info.""" """Ensure valid hints are accepted for discovery_info."""
type_hint_checker.module = "homeassistant.components.pylint_test.device_trigger"
func_node = astroid.extract_node( func_node = astroid.extract_node(
""" """
async def async_get_triggers( #@ async def async_get_triggers( #@
@ -235,8 +270,10 @@ def test_valid_list_dict_str_any(
device_id: str device_id: str
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
pass pass
""" """,
"homeassistant.components.pylint_test.device_trigger",
) )
type_hint_checker.visit_module(func_node.parent)
with assert_no_messages(linter): with assert_no_messages(linter):
type_hint_checker.visit_asyncfunctiondef(func_node) type_hint_checker.visit_asyncfunctiondef(func_node)