2

I have a base class to provide functionality to a variety of classes, and I want to make sure classes that inherit from this one are decorated with @dataclass. The following definition results in the ValueError being raised:

from dataclasses import dataclass, is_dataclass


class Base:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not is_dataclass(cls):
            raise ValueError('Base subclasses should be dataclasses.')


@dataclass
class Child(Base):
    ...

As I understand, this is due to the inheritance happening (and therefore Base.__init_subclass__(Child) running) before the @dataclass has a chance to run. Is there a nice way to work around this?

Dunya Degirmenci
  • 365
  • 4
  • 20
  • 2
    Why does it matter? There's nothing special about a dataclass; the decorator just generates some boilerplate code for you. – chepner Jul 30 '19 at 13:07
  • @chepner Could be important if you expect to use the instances with `dataclasses.fields` or such… – deceze Jul 30 '19 at 13:12
  • 1
    are you actually writing a base class, or are you writing a [mixin](https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful)? – Arne Aug 02 '19 at 13:26

1 Answers1

2

Perhaps you can decorate your classes. Here's the idea (adjust accordingly).

Given

import dataclasses as dc

Code

def verify_dataclass(kls):
    if not dc.is_dataclass(kls):
        print(f"'{kls.__name__}' is not a dataclass.")
        # Do something
    return kls

Demo

A dataclass is ok.

@verify_dataclass
@dc.dataclass
class Foo:
    pass

A subclass of a dataclass is ok.

@verify_dataclass
class Bar(Foo):
    pass

A regular class warns.

@verify_dataclass
class Baz:
    pass

# 'Baz' is not a dataclass.
pylang
  • 40,867
  • 14
  • 129
  • 121