diff --git a/pylint/plugins/hass_enforce_type_hints.py b/pylint/plugins/hass_enforce_type_hints.py index e230c27b4ee..24b32f2e238 100644 --- a/pylint/plugins/hass_enforce_type_hints.py +++ b/pylint/plugins/hass_enforce_type_hints.py @@ -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.` + # Or `homeassistant.components..` + 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): diff --git a/tests/pylint/test_enforce_type_hints.py b/tests/pylint/test_enforce_type_hints.py index feb3b6b341c..0bd273985e3 100644 --- a/tests/pylint/test_enforce_type_hints.py +++ b/tests/pylint/test_enforce_type_hints.py @@ -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)