mypy doesn't understand class and interface are the same

Question:

from abc import ABC, abstractmethod


class IState(ABC):
    """Interface para o padrão de projeto State."""

    @abstractmethod
    def sucesso_ao_pagar(self) -> None:
        pass

    @abstractmethod
    def despachar_pedido(self) -> None:
        pass

    @abstractmethod
    def cancelar_pedido(self) -> None:
        pass


class Pedido:
    """Classe que representa um pedido."""

    def __init__(self) -> None:
        self.estado_atual = AguardandoPagamentoState(self)

    def realizar_pagamento(self) -> None:
        self.estado_atual.sucesso_ao_pagar()

    def despachar(self) -> None:
        self.estado_atual.despachar_pedido()

    def cancelar(self) -> None:
        self.estado_atual.cancelar_pedido()

    def set_estado_atual(self, estado_atual: IState) -> None:
        self.estado_atual = estado_atual

    def __str__(self) -> str:
        return str(self.estado_atual)


class AguardandoPagamentoState(IState):
    """Estado inicial do pedido."""

    def __init__(self, meu_pedido: Pedido):
        self.pedido = meu_pedido

    def sucesso_ao_pagar(self) -> None:
        print("Pedido pago com sucesso!")
        self.pedido.set_estado_atual(PagoState(self.pedido))

    def despachar_pedido(self) -> None:
        print("Pedido não pode ser despachado, pois não foi pago!")

    def cancelar_pedido(self) -> None:
        print("Pedido cancelado com sucesso!")
        self.pedido.set_estado_atual(CanceladoState(self.pedido))

This code works fine, but when i run mypy, it states:

Incompatible types in assignment (expression has type "IState", variable has type "AguardandoPagamentoState") [assignment]mypy

Why is it generating this problem, given that the AguardandoPagamentoState class implements the IState interface?

If I put a comment like

# type: ignore

it stops throwing this error, but I want to know how solve this.
Mypy should understand the class relation

Asked By: Tó Rugain

||

Answers:

The problem is that you establish a static type of AguardandoPagamentoState for the attribute in Pedido.__init__:

def __init__(self) -> None:
    self.estado_atual = AguardandoPagamentoState(self)

But then you try to relax that by allowing any value of type IState to be assigned to it in set_estado_atual:

def set_estado_atual(self, estado_atual: IState) -> None:
    self.estado_atual = estado_atual

AguardandoPagamentoState may implement the interface, but not all types that implement the interface are AguardandoPagamentoState or subclasses thereof. You need to establish the more general static type explicitly:

def __init__(self) -> None:
    self.estado_atual: IState = AguardandoPagamentoState(self)
Answered By: chepner
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.