mypy: Signature of "__setitem__" incompatible with supertype "list"

Viewed 352

I'm trying to use a specialised subclass of List to create a TokenList that is backed by a regular list, but also contains a metadata dict with key/values. Everything works great, until I wanted to add types to this, and now I'm getting this error:

error: Signature of "__setitem__" incompatible with supertype "list"

... and the error is the first overload below:

import typing as T

class Token(dict):
    ...

class TokenList(T.List[Token]):
    ...

    @T.overload
    def __setitem__(self, key: int, tokens: T.Union[dict, Token]) -> None: ...  # noqa, pragma: no cover

    @T.overload
    def __setitem__(self, key: slice, tokens: T.Union[T.Iterable[T.Union[dict, Token]], 'TokenList']) -> None: ...  # noqa, pragma: no cover

    def __setitem__(self, key, tokens):  # noqa: F811
        if isinstance(key, int):
            token = tokens
            token = self._dict_to_token_and_set_defaults(token)
            super(TokenList, self).__setitem__(key, token)
        else:
            tokens = [self._dict_to_token_and_set_defaults(token) for token in tokens]
            super(TokenList, self).__setitem__(key, tokens)

I've tried to find the type defintition for list to compare it to mine, but hasn't been able to find it.

Except for ignoring this error and going on with my life, is there any way around this? Any pointers appreciated.

(I'm using mypy 0.910 and Python 3.9.1 if that matters.)

1 Answers

You can find out the static type of any object with reveal_type. So trying:

reveal_type(list.__setitem__)

Gives you:

reveal_type.py:2: note: Revealed type is "Overload(def [_T] (builtins.list[_T`1], typing_extensions.SupportsIndex, _T`1), def [_T] (builtins.list[_T`1], builtins.slice, typing.Iterable[_T`1]))"

The problem with your original code is that lists can be indexed by more than just ints and slices: they support any object that can be coerced into integers with __index__ as well.

So that means you need to make two changes: import typing_extensions and replace int with typing_extensions.SupportsIndex.

import typing_extensions as TE

...

def __setitem__(self, key: TE.SupportsIndex, tokens: T.Union[dict, Token]) -> None: ...  # noqa, pragma: no cover

Also, that means that your implementation isn't quite correct, since non-integer non-slice indexes will be treated as slice indexes. I would write it as follows:

def __setitem__(self, key, tokens):  # noqa: F811
    if isinstance(key, slice):
        tokens = [self._dict_to_token_and_set_defaults(token) for token in tokens]
    else:
        tokens = self._dict_to_token_and_set_defaults(tokens)
    super().__setitem__(key, tokens)
Related