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'.
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.
Cool, thanks https://github.com/PyTorchLightning/pytorch-lightning/issues/854#issuecomment-589563317
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!
Most helpful comment
Thanks for your persistence. This is indeed a bug. I'll work on a fix.