mypy @overloads calling other @overloads in __setitem__ gives error

  mypy, overloading, python, python-3.10, python-3.x

I’m making a class that implements the MutableSequence contract and am having trouble type-hinting the getters & setters which take either a slice or a SupportsIndex instance as the index argument. In my application I have a _normalise_index function which turns negative relative indices and slices into positive ones by subtracting from len – e.g. collection[:-1] -> collection[:5], but mypy is not happy about my overloads…

Here is a simplified example:

from typing import Iterable, SupportsIndex, TypeVar, overload

T = TypeVar("T")


class Example:  # in real code this is `class Example(typing.MutableSequence[T]): but that's TMI for this minrepro`
    def __init__(self, iterable: Iterable[T]):
        self._lst = list(iterable)

    def __len__(self) -> int:
        return len(self._lst)

    # fmt: off
    @overload
    def _normalise_index(self, index: slice) -> slice: ...
    @overload
    def _normalise_index(self, index: SupportsIndex) -> int: ...
    # fmt: on

    def _normalise_index(self, index: slice | SupportsIndex) -> slice | int:
        "Convert -ve to +ve index - this makes sense for the real code..."
        if isinstance(index, slice):
            return slice(
                0 if not index.start else self._normalise_index(index.start),
                len(self) if not index.stop else self._normalise_index(index.stop),
                index.step or 1,
            )
        else:
            return len(self) + idx if (idx := int(index)) < 0 else idx

    # fmt: off
    @overload
    def __setitem__(self, unsafe_index: SupportsIndex, value: T) -> None: ...
    @overload
    def __setitem__(self, unsafe_index: slice, value: Iterable[T]) -> None: ...
    # fmt: on

    def __setitem__(  # line 38
        self, unsafe_index: SupportsIndex | slice, value: T | Iterable[T]
    ) -> None:
        normalised_index = self._normalise_index(unsafe_index)
        self._lst[normalised_index] = value  # line 42

… which gives 3 errors:

main.py:38: error: Overloaded function implementation does not accept all possible arguments of signature 2
main.py:42: error: Invalid index type "Union[int, slice]" for "List[T]"; expected type "SupportsIndex"
main.py:42: error: Incompatible types in assignment (expression has type "Union[T, Iterable[T]]", target has type "T")

View in online mypy playground


It looks like mypy is ignoring my overloads for __setitem__ so it thinks unsafe_index could be a slice or a SupportsIndex and then it is only matching one of the overloads for _normalise_index. No idea why…

Source: Python-3x Questions

LEAVE A COMMENT