pythonpython-typingmypyliskov-substitution-principle

Why does mypy not complain when overloading an abstract method's concrete implementation in the child class?


I am trying to wrap my head around the @overload operator, generics and the Liskov Substitution Principle. I usually make my abstract classes generic on some typevar T which is followed by concrete implementations of child classes.

For example please consider the following code:

from typing import TypeVar

E = TypeVar("E")  # entity

class AbstractRepository(ABC, Generic[E]):
    @abstractmethod
    async def update(self, entity: E) -> bool:
        raise NotImplementedError

class IndexRepository(AbstractRepository[str]):
    @overload
    async def update(self, index: str) -> bool:
        ...

    @overload
    async def update(self, index: list[str]) -> bool:
        ...

    async def update(self, index: Union[str, list[str]]) -> bool:
         # implementation here
        return stuff

I expect mypy to complain about the Liskov Substitution Principle, but it does not, even though the implementation of the update method the parameter index has a type Union[str, list[str]] and the child class IndexRepository(AbstractRepository[str])'s signature defines T=str.

Is this because of the @overload operator, and why is LSP not violated here?


Solution

  • Therefore, IndexRepository.update is consistent with IndexRepository being a subtype of AbstractRepository and does not violate the LSP.

    See also Why is `Callable` generic type contravariant in the arguments?