コード例 #1
0
def test_number_entity(linter: UnittestLinter, type_hint_checker: BaseChecker) -> None:
    """Ensure valid hints are accepted for number entity."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    # Ensure that device class is valid despite Entity inheritance
    # Ensure that `int` is valid for `float` return type
    class_node = astroid.extract_node(
        """
    class Entity():
        pass

    class RestoreEntity(Entity):
        pass

    class NumberEntity(Entity):
        pass

    class MyNumber( #@
        RestoreEntity, NumberEntity
    ):
        @property
        def device_class(self) -> NumberDeviceClass:
            pass

        @property
        def native_value(self) -> int:
            pass
    """,
        "homeassistant.components.pylint_test.number",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #2
0
def test_valid_config_flow_step(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure valid hints are accepted for ConfigFlow step."""
    class_node = astroid.extract_node(
        """
    class FlowHandler():
        pass

    class ConfigFlow(FlowHandler):
        pass

    class AxisFlowHandler( #@
        ConfigFlow, domain=AXIS_DOMAIN
    ):
        async def async_step_zeroconf(
            self,
            device_config: ZeroconfServiceInfo
        ) -> FlowResult:
            pass
    """,
        "homeassistant.components.pylint_test.config_flow",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #3
0
def test_media_player_entity(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure valid hints are accepted for media_player entity."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node = astroid.extract_node(
        """
    class Entity():
        pass

    class MediaPlayerEntity(Entity):
        pass

    class MyMediaPlayer( #@
        MediaPlayerEntity
    ):
        async def async_get_media_image(self) -> tuple[bytes | None, str | None]:
            pass
    """,
        "homeassistant.components.pylint_test.media_player",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #4
0
def test_valid_mapping_return_type(
    linter: UnittestLinter,
    type_hint_checker: BaseChecker,
    return_hint: str,
) -> None:
    """Check that Mapping[xxx, Any] accepts both Mapping and dict."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node = astroid.extract_node(
        f"""
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class FanEntity(ToggleEntity):
        pass

    class MyFanA( #@
        FanEntity
    ):
        @property
        def capability_attributes(
            self
        ){return_hint}:
            pass
    """,
        "homeassistant.components.pylint_test.fan",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #5
0
def test_invalid_long_tuple(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Check invalid entity properties are ignored by default."""
    # Set ignore option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node, rgbw_node, rgbww_node = astroid.extract_node(
        """
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class LightEntity(ToggleEntity):
        pass

    class TestLight( #@
        LightEntity
    ):
        @property
        def rgbw_color( #@
            self
        ) -> tuple[int, int, int, int, int]:
            pass

        @property
        def rgbww_color( #@
            self
        ) -> tuple[int, int, int, int, float]:
            pass
    """,
        "homeassistant.components.pylint_test.light",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_adds_messages(
        linter,
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=rgbw_node,
            args=(["tuple[int, int, int, int]", None], "rgbw_color"),
            line=15,
            col_offset=4,
            end_line=15,
            end_col_offset=18,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=rgbww_node,
            args=(["tuple[int, int, int, int, int]", None], "rgbww_color"),
            line=21,
            col_offset=4,
            end_line=21,
            end_col_offset=19,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #6
0
def test_invalid_config_flow_async_get_options_flow(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure invalid hints are rejected for ConfigFlow async_get_options_flow."""
    # AxisOptionsFlow doesn't inherit OptionsFlow, and therefore should fail
    class_node, func_node, arg_node = astroid.extract_node(
        """
    class FlowHandler():
        pass

    class ConfigFlow(FlowHandler):
        pass

    class OptionsFlow(FlowHandler):
        pass

    class AxisOptionsFlow():
        pass

    class AxisFlowHandler( #@
        ConfigFlow, domain=AXIS_DOMAIN
    ):
        def async_get_options_flow( #@
            config_entry #@
        ) -> AxisOptionsFlow:
            return AxisOptionsFlow(config_entry)
    """,
        "homeassistant.components.pylint_test.config_flow",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_adds_messages(
        linter,
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=arg_node,
            args=(1, "ConfigEntry", "async_get_options_flow"),
            line=18,
            col_offset=8,
            end_line=18,
            end_col_offset=20,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=func_node,
            args=("OptionsFlow", "async_get_options_flow"),
            line=17,
            col_offset=4,
            end_line=17,
            end_col_offset=30,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #7
0
def test_invalid_config_flow_step(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure invalid hints are rejected for ConfigFlow step."""
    class_node, func_node, arg_node = astroid.extract_node(
        """
    class FlowHandler():
        pass

    class ConfigFlow(FlowHandler):
        pass

    class AxisFlowHandler( #@
        ConfigFlow, domain=AXIS_DOMAIN
    ):
        async def async_step_zeroconf( #@
            self,
            device_config: dict #@
        ):
            pass
    """,
        "homeassistant.components.pylint_test.config_flow",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_adds_messages(
        linter,
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=arg_node,
            args=(2, "ZeroconfServiceInfo", "async_step_zeroconf"),
            line=13,
            col_offset=8,
            end_line=13,
            end_col_offset=27,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=func_node,
            args=("FlowResult", "async_step_zeroconf"),
            line=11,
            col_offset=4,
            end_line=11,
            end_col_offset=33,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #8
0
def test_invalid_mapping_return_type(
    linter: UnittestLinter,
    type_hint_checker: BaseChecker,
    return_hint: str,
) -> None:
    """Check that Mapping[xxx, Any] doesn't accept invalid Mapping or dict."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node, property_node = astroid.extract_node(
        f"""
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class FanEntity(ToggleEntity):
        pass

    class MyFanA( #@
        FanEntity
    ):
        @property
        def capability_attributes( #@
            self
        ){return_hint}:
            pass
    """,
        "homeassistant.components.pylint_test.fan",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_adds_messages(
        linter,
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=property_node,
            args=(["Mapping[str, Any]", None], "capability_attributes"),
            line=15,
            col_offset=4,
            end_line=15,
            end_col_offset=29,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #9
0
def test_invalid_device_class(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure invalid hints are rejected for entity device_class."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node, prop_node = astroid.extract_node(
        """
    class Entity():
        pass

    class CoverEntity(Entity):
        pass

    class MyCover( #@
        CoverEntity
    ):
        @property
        def device_class( #@
            self
        ):
            pass
    """,
        "homeassistant.components.pylint_test.cover",
    )
    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=(["CoverDeviceClass", "str", None], "device_class"),
            line=12,
            col_offset=4,
            end_line=12,
            end_col_offset=20,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #10
0
def test_valid_long_tuple(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Check invalid entity properties are ignored by default."""
    # Set ignore option
    type_hint_checker.config.ignore_missing_annotations = False

    class_node, _, _ = astroid.extract_node(
        """
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class LightEntity(ToggleEntity):
        pass

    class TestLight( #@
        LightEntity
    ):
        @property
        def rgbw_color( #@
            self
        ) -> tuple[int, int, int, int]:
            pass

        @property
        def rgbww_color( #@
            self
        ) -> tuple[int, int, int, int, int]:
            pass
    """,
        "homeassistant.components.pylint_test.light",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #11
0
def test_valid_config_flow_async_get_options_flow(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Ensure valid hints are accepted for ConfigFlow async_get_options_flow."""
    class_node = astroid.extract_node(
        """
    class FlowHandler():
        pass

    class ConfigFlow(FlowHandler):
        pass

    class OptionsFlow(FlowHandler):
        pass

    class AxisOptionsFlow(OptionsFlow):
        pass

    class OtherOptionsFlow(OptionsFlow):
        pass

    class AxisFlowHandler( #@
        ConfigFlow, domain=AXIS_DOMAIN
    ):
        def async_get_options_flow(
            config_entry: ConfigEntry
        ) -> AxisOptionsFlow | OtherOptionsFlow | OptionsFlow:
            if self.use_other:
                return OtherOptionsFlow(config_entry)
            return AxisOptionsFlow(config_entry)

    """,
        "homeassistant.components.pylint_test.config_flow",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #12
0
def test_vacuum_entity(linter: UnittestLinter, type_hint_checker: BaseChecker) -> None:
    """Ensure valid hints are accepted for vacuum entity."""
    # Set bypass option
    type_hint_checker.config.ignore_missing_annotations = False

    # Ensure that `dict | list | None` is valid for params
    class_node = astroid.extract_node(
        """
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class _BaseVacuum(Entity):
        pass

    class VacuumEntity(_BaseVacuum, ToggleEntity):
        pass

    class MyVacuum( #@
        VacuumEntity
    ):
        def send_command(
            self,
            command: str,
            params: dict[str, Any] | list[Any] | None = None,
            **kwargs: Any,
        ) -> None:
            pass
    """,
        "homeassistant.components.pylint_test.vacuum",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_no_messages(linter):
        type_hint_checker.visit_classdef(class_node)
コード例 #13
0
def test_ignore_invalid_entity_properties(
    linter: UnittestLinter, type_hint_checker: BaseChecker
) -> None:
    """Check invalid entity properties are ignored by default."""
    # Set ignore option
    type_hint_checker.config.ignore_missing_annotations = True

    class_node = astroid.extract_node(
        """
    class Entity():
        pass

    class LockEntity(Entity):
        pass

    class DoorLock( #@
        LockEntity
    ):
        @property
        def changed_by(
            self
        ):
            pass

        async def async_lock(
            self,
            **kwargs
        ) -> bool:
            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)
コード例 #14
0
def test_named_arguments(
    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, func_node, percentage_node, preset_mode_node = astroid.extract_node(
        """
    class Entity():
        pass

    class ToggleEntity(Entity):
        pass

    class FanEntity(ToggleEntity):
        pass

    class MyFan( #@
        FanEntity
    ):
        async def async_turn_on( #@
            self,
            percentage, #@
            *,
            preset_mode: str, #@
            **kwargs
        ) -> bool:
            pass
    """,
        "homeassistant.components.pylint_test.fan",
    )
    type_hint_checker.visit_module(class_node.parent)

    with assert_adds_messages(
        linter,
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=percentage_node,
            args=("percentage", "int | None", "async_turn_on"),
            line=16,
            col_offset=8,
            end_line=16,
            end_col_offset=18,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=preset_mode_node,
            args=("preset_mode", "str | None", "async_turn_on"),
            line=18,
            col_offset=8,
            end_line=18,
            end_col_offset=24,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=func_node,
            args=("kwargs", "Any", "async_turn_on"),
            line=14,
            col_offset=4,
            end_line=14,
            end_col_offset=27,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=func_node,
            args=("None", "async_turn_on"),
            line=14,
            col_offset=4,
            end_line=14,
            end_col_offset=27,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)
コード例 #15
0
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, func_node = astroid.extract_node(
        """
    class Entity():
        pass

    class LockEntity(Entity):
        pass

    class DoorLock( #@
        LockEntity
    ):
        @property
        def changed_by( #@
            self
        ):
            pass

        async def async_lock( #@
            self,
            **kwargs
        ) -> bool:
            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], "changed_by"),
            line=12,
            col_offset=4,
            end_line=12,
            end_col_offset=18,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-argument-type",
            node=func_node,
            args=("kwargs", "Any", "async_lock"),
            line=17,
            col_offset=4,
            end_line=17,
            end_col_offset=24,
        ),
        pylint.testutils.MessageTest(
            msg_id="hass-return-type",
            node=func_node,
            args=("None", "async_lock"),
            line=17,
            col_offset=4,
            end_line=17,
            end_col_offset=24,
        ),
    ):
        type_hint_checker.visit_classdef(class_node)