Why can't incorrect MRO not be detected by a static type checker like mypy?

132 views Asked by At

Consider the following scenario:

from dataclasses import dataclass
from typing import Generic, Protocol, TypeVar

T = TypeVar("T")


class HasFoo(Protocol):
    foo: str


class Mixin(HasFoo):
    foo = "test"


@dataclass()
class MyClass(Generic[T], Mixin):
    t: T

If you execute this code, you get the following error:

Traceback (most recent call last):
  File "/home/veith/.config/JetBrains/PyCharm2022.2/scratches/scratch_4.py", line 16, in <module>
    class MyClass(Generic[T], FooMixin):
  File "/home/veith/.pyenv/versions/3.8.3/lib/python3.8/abc.py", line 85, in __new__
    cls = super().__new__(mcls, name, bases, namespace, **kwargs)
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Generic, Mixin

Process finished with exit code 1

I guess this has something to do with the fact that Protocol can also take generic arguments like this:

class HasT(Protocol[T]):
    t: T

# sugar for
class HasT(Protocol, Generic[T]):
    t: T

# also works like this, which I would have expected to cause the same issue, but doesn't
class HasT(Generic[T], Protocol):
    t: T

I could not find info on this in the PEP for protocols.

The answer to make it work is actually rather simple: Just reverse the order of inheritance in your code, so it looks like this:

@dataclass()
class MyClass(Mixin, Generic[T]):
    t: T

So why is this happening, and why does it type-check using mypy 0.980? The linked question does not answer my question about why it type-checks. Generic inherits nothing, and Protocol inherits Generic. So I see how the inheritance "tree" has a loop, but why does mypy not recognize this?

0

There are 0 answers