Search code examples
pythonmypypython-typingpython-dataclasses

What is the right way of annotating constructor of abstract class to make Mypy happy?


I have an abstract base class BsaeFoo that my dataclasses such as ChildFoo is inheriting from.

from abc import ABCMeta
from typing import Dict, Any, TypeVar, Type
from dataclasses import dataclass

Foo = TypeVar("Foo", bound="BaseFoo")


class BaseFoo(metaclass=ABCMeta):
    @classmethod
    def from_dict(cls: Type[Foo], data: Dict[str, Any]) -> Foo:
        init_data = {key: data.get(key, None) for key in cls.__annotations__}
        return cls(**init_data)


@dataclass
class ChildFoo(BaseFoo):
    x: int
    y: int

Running Mypy gives me

main.py:12: error: Too many arguments for "BaseFoo"
Found 1 error in 1 file (checked 1 source file)

from_dict would be an alternative constructor of the child dataclasses. It reads the input dict based on the cls.__annotations__ attribute, so it won't raise in the child classes. As for BaseFoo itself, it does not make sense to check initialization since it is an abstract class.

How do I make BaseFoo.from_dict pass Mypy without using # type: ignore ?


Solution

  • Add explicit definition of __init__ in BaseFoo

    class BaseFoo(ABC):
        def __init__(self, **kwargs: Any) -> None:
            raise NotImplementedError
    
        @classmethod
        def from_dict(cls: type[Foo], data: Dict[str, Any]) -> Foo:
            init_data = {key: data.get(key, None) for key in cls.__annotations__}
            return cls(**init_data)