Pyright: Cannot instantiate abstract class

Created on 18 Feb 2020  路  9Comments  路  Source: microsoft/pyright

Pyright (last version as of today) reports an error related to abstract classes but I am not sure the error is relevant:

import abc


class MixinA(abc.ABC):
    @abc.abstractmethod
    def a(self) -> None:
        print('MixinA.a')


class MixinB(abc.ABC):
    @abc.abstractmethod
    def b(self) -> None:
        print('MixinB.b')


class Trainer(MixinA, MixinB):
    pass


ab = Trainer()  # <--- Error is here

The error is Cannot instantiate abstract class 'Trainer'.

bug

Most helpful comment

Thanks for your persistence. This is indeed a bug. I'll work on a fix.

All 9 comments

If you run the code above, the python interpreter will throw an exception at the specified line. Here's the error message: "TypeError: Can't instantiate abstract class Trainer with abstract methods a, b".

Pyright is doing the right thing here by telling you that you can't instantiate an abstract class. You must provide methods in Trainer that override a and b.

Sorry, my example isn't really reproducing my more complex use case. Consider this example:

import abc


class MixinA(abc.ABC):
    @abc.abstractmethod
    def get_model(self):
        pass


class MixinB(abc.ABC):
    def get_model(self):
        print('MixinB.get_model')

class MixinC(abc.ABC):
    @abc.abstractmethod
    def get_model(self):
        pass

class MixinD(abc.ABC):
    pass


# Trainer_1
class Trainer_1(MixinA, MixinB, MixinC, MixinD):
    pass

trainer = Trainer_1()  # <- Cannot instantiate abstract class 'Trainer_1' 'MixinC.get_model' is abstract

# Trainer_2
class Trainer_2(MixinA, MixinB):
    pass

trainer = Trainer_2()  # <- No error here

So it seems the error is raised when for a defined method there are more parent classes with this method as abstract than parent classes defining this method.

If you run your second example in the python interpreter, it also generates an exception:

Traceback (most recent call last):
  File "test2.py", line 27, in <module>
    trainer = Trainer_1()  # <- Cannot instantiate abstract class 'Trainer_1' 'MixinC.get_model' is abstract

So once again, I think Pyright is doing the right thing by flagging this as an error.

Sorry to make look at this again but I think I got it right now. I found a situation where Pyright complains while Python does not raise an error and another situation where it's the opposite:

import abc

class MixinA(abc.ABC):
    pass

class MixinB(abc.ABC):
    def get_model(self):
        print('MixinB.get_model')

class MixinC(abc.ABC):
    @abc.abstractmethod
    def get_model(self):
        pass

    def use_model(self):
        m = self.get_model()
        print('MixinC.get_model')

# correct parent class order
class Trainer_1a(MixinA, MixinB, MixinC):
    pass

trainer = Trainer_1a()  # <- PYTHON: no error | PYRIGHT: error

# wrong parent class order
class Trainer_1b(MixinA, MixinC, MixinB):
    pass

trainer = Trainer_1b()  # <- PYTHON: error | PYRIGHT: no error

(Actually, it's @Borda who found this)

Thanks for your persistence. This is indeed a bug. I'll work on a fix.

This is fixed in 1.1.25, which I just published. I discovered another related bug when I was doing my testing. Pyright is currently not honoring proper MRO ordering, it's doing a recursive order (similar to how Python 2.x did member lookups). There are edge cases where the behaviors differ. I've logged a separate bug and will get that fixed in an upcoming release.

Thanks @erictraut for the prompt fix!

Was this page helpful?
0 / 5 - 0 ratings