Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement overload of __setitem__ for MutableSequence? #7858

Open
asottile opened this issue Nov 2, 2019 · 3 comments
Open

How to implement overload of __setitem__ for MutableSequence? #7858

asottile opened this issue Nov 2, 2019 · 3 comments

Comments

@asottile
Copy link
Contributor

asottile commented Nov 2, 2019

I'm trying to implement my own MutableSequence but struggling with the proper way to implement the @overload

Here's my code example:

from typing import Iterable
from typing import List
from typing import MutableSequence
from typing import Union
from typing import overload


class RecordBehaviourList(MutableSequence[str]):
    def __init__(self, lst: List[str]) -> None:
        self._lst = lst

    @overload
    def __getitem__(self, index: int) -> str:
        ...

    @overload  # noqa: F811  # TODO: flake8 3.8
    def __getitem__(self, index: slice) -> MutableSequence[str]:
        ...

    def __getitem__(  # noqa: F811  # TODO: flake8 3.8
            self,
            index: Union[int, slice],
    ) -> Union[str, MutableSequence[str]]:
        return self._lst[index]

    @overload
    def __setitem__(self, index: int, value: str) -> None:
        ...

    @overload  # noqa: F811  # TODO: flake8 3.8
    def __setitem__(self, index: slice, value: Iterable[str]) -> None:
        ...

    def __setitem__(  # noqa: F811  # TODO: flake8 3.8
            self,
            index: Union[int, slice],
            value: Union[str, Iterable[str]],
    ) -> None:
        self._lst[index] = value

    def __delitem__(self, index: Union[int, slice]) -> None:
        del self._lst[index]

    def insert(self, index: int, value: str) -> None:
        self._lst.insert(index, value)

I've taken the overload definitions from this example (?) but perhaps I'm doing this incorrectly?

For now I can # type: ignore but seems non-ideal

Here's my version information and current output

$ mypy --version
mypy 0.740
$ mypy t3.py
t3.py:39: error: Invalid index type "Union[int, slice]" for "List[str]"; expected type "int"
t3.py:39: error: Incompatible types in assignment (expression has type "Union[str, Iterable[str]]", target has type "str")
Found 2 errors in 1 file (checked 1 source file)
@ilevkivskyi
Copy link
Member

Mypy doesn't use overload information to type-check the body. For such cases you can use a trick:

if isinstance(index, int):
    assert isinstance(value, str)
    self._lst[index] = value
else:
    assert not isinstance(value, str)
    self._lst[index] = value

This is kind of ugly however. I think we can use the overload information and type-check body multiple times in simple cases, i.e. if neither *args nor **kwargs appear in any of the overloads and in the implementation.

cc @Michael0x2a

@Greedquest
Copy link

@ilevkivskyi Is there a way to do this within mypy which is a runtime no-op or as close as possible, perhaps with TypeGuard or cast or something?

@ilevkivskyi
Copy link
Member

You can use if TYPE_CHECKING: ... for something to have effect in mypy but not at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants