Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get an abstract dataclass to pass mypy?

mypy v0.910 rejects abstract dataclasses in Python 3.9. Here's the Minimal Reproducible Example:

from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class Liquid(ABC):

    @abstractmethod
    def drip(self) -> None:
        pass

Here's the error message:

$ mypy --python-version 3.9 so.py
so.py:4: error: Only concrete class can be given where "Type[Liquid]" is expected
Found 1 error in 1 file (checked 1 source file)

How do I get this code to pass mypy?


Notes

I gather from mypy issue #5374 that this is a bug in mypy, first noticed in 2018 and still not corrected. I figure that people must be using mypy with abstract dataclasses, though, so there must be a workaround or a correct way to define or annotate the class. What is recommended?

The basis for the error message seems to be that mypy assumes that any object of type Type can be instantiated, but abstract classes cannot be instantiated. This appears to be the error since Type is defined to mean a class object, not necessarily a concrete class object (i.e. one that can be instantiated).

Adding # type: ignore to the line containing class Liquid does not block the error message. Since the code does not contain Type[Liquid], I figure it must be in the code generated by dataclass. Type is deprecated in Python 3.9, but apparently the dataclass code generator still generates it.

like image 202
Ben Kovitz Avatar asked Jan 16 '26 21:01

Ben Kovitz


2 Answers

Create a dataclass as a mixin and let the ABC inherit from it:

from abc import ABC, abstractmethod
from dataclasses import dataclass    

@dataclass
class LiquidDataclassMixin:
    my_var: str

class Liquid(ABC, LiquidDataclassMixin):
    @abstractmethod
    def drip(self) -> None:
        pass

This works with mypy type checking as well. I recommend against using # type: ignore as that defeats the point of type-checking. Taken from this GitHub issue.

like image 92
Nicolas Forstner Avatar answered Jan 19 '26 15:01

Nicolas Forstner


Add # type: ignore to the decorator line. So in your case it would be:

from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass  # type: ignore[misc]
class Liquid(ABC):

    @abstractmethod
    def drip(self) -> None:
        pass
like image 31
Kamil Tagowski Avatar answered Jan 19 '26 15:01

Kamil Tagowski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!