7

I have a subclass of list that adds some administrative stuff on the components of the list. Everything works OK, but mypy complains about signature of the super call to __setitem__. Here is the problem reduced to its bare minimum:

from typing import List, Iterable, Union, overload
from typing_extensions import SupportsIndex


class MyData:
    pass


class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self, index: SupportsIndex, value: MyData) -> None: ...

    @overload
    def __setitem__(self, index: slice, value: Iterable[MyData]) -> None: ...

    def __setitem__(self, index: Union[SupportsIndex, slice], value: Union[MyData, Iterable[MyData]]) -> None:
        # Administrative stuff deleted
        super().__setitem__(index, value)

    def __delitem__(self, index: Union[SupportsIndex, slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)

When I run mypy on this, I get:

src\seq.py:18: error: Invalid index type "Union[SupportsIndex, slice]" for "MyDataSeq"; expected type "SupportsIndex"
src\seq.py:18: error: Incompatible types in assignment (expression has type "Union[MyData, Iterable[MyData]]", target has type "MyData")
Found 2 errors in 1 file (checked 1 source file)

I'm at a loss here, because obviously __setitem__, just like __delitem__, accepts both an int-like (SupportsIndex) and a slice object as its first argument. It is almost as if mypy somehow reaches the conclusion that only int-like objects are supported -- that matches with the second error that it expects only a MyData as second argument, not Iterable[MyData].

I have tried this on both Python 3.7 and 3.9, the errors are the same. I can of course tell mypy to ignore these errors, but I really would like to know what causes this, and how to solve it. Any ideas?

2 Answers 2

1

As for this similar issue, Mypy doesn't use overload information to type-check the function body.

To solve your problem, you could give a hint to Mypy what types you are passing to it by means of isinstance. Like so:

from typing import List, Iterable, Union, overload
from typing_extensions import SupportsIndex


class MyData:
    pass


class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self, index: SupportsIndex, value: MyData) -> None: ...

    @overload
    def __setitem__(self, index: slice, value: Iterable[MyData]) -> None: ...

    def __setitem__(self, index: Union[SupportsIndex, slice], value: Union[MyData, Iterable[MyData]]) -> None:
        # Administrative stuff deleted
        if isinstance(index, slice) and isinstance(value, Iterable):
            super().__setitem__(index, value)
        elif isinstance(index, int) and isinstance(value, MyData):
            super().__setitem__(index, value)
        else:
            raise TypeError(f"{index}/{value} Invalid index/value type.")

    def __delitem__(self, index: Union[SupportsIndex, slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)
Sign up to request clarification or add additional context in comments.

Comments

-1

You don't need to annotate the implementation, only the decorated definitions.

class MyDataSeq(List[MyData]):
    @overload
    def __setitem__(self, index: SupportsIndex, value: MyData) -> None:
        ...

    @overload
    def __setitem__(self, index: slice, value: Iterable[MyData]) -> None:
       ...

    def __setitem__(self, index, value):
        # Administrative stuff deleted
        super().__setitem__(index, value)

    def __delitem__(self, index: Union[SupportsIndex, slice]) -> None:
        # Administrative stuff deleted
        super().__delitem__(index)

(This will leave a similar problem for __delitem__ to be resolved, but I think that's a matter for a separate question.)

1 Comment

It is not an actual solution. Annotating implementation is a way to go, because otherwise implementation is not checked (without flag --check-untyped-defs), so you don't get all type checking benefits. mypy team recommends annotating implementation with Unions of overloads types. And it doesn't actually answer the question, because your answer just suggests something similar to # type: ignore (and even worse, because type checking is completely silenced without explicit marker), and does not explain why this happens. (downvote mine)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.