mypy error: Cannot instantiate abstract class with abstract attributes when adding types to an abstract method

Viewed 664

I have a class I would like to add typehints to that looks as following:

import yaml

class TestClass(dict):

    @classmethod
    def load(cls, fname) -> "TestClass":
        return cls(yaml.safe_load(""))

    @property
    @abc.abstractmethod
    def test(self):
        raise

when I run mypy on a module containing only this class I get the following error message:

error: Cannot instantiate abstract class 'TestClass' with abstract attribute 'test'

From what I have understood from other posts this has something to do with when the 'test' method is instantiated when executing the load method. Is there a way to fix this issue using typehints alone or would I need to adjust my code here?

1 Answers

What mypy is telling you is that TestClass.load(...) will fail, because it will try to create an instance of the abstract class TestClass.

We can fix this by requiring that cls can be called with whatever yaml.safe_load returns (I'm assuming dict here), and returns an instance of TestClass:

from typing import Callable
import abc
import yaml

class TestClass(dict):

    @classmethod
    def load(cls: Callable[[dict], TestClass], fname) -> "TestClass":
        return cls(yaml.safe_load(""))

    @property
    @abc.abstractmethod
    def test(self):
        raise

Note that TestClass.load("foo") will now also pass type checking. This is fair to some extent, because it's also fine at runtime, until you call test() on the created instance. I think this might be a limitation of how mypy implements protocols.

Related