Following my rant regarding how dataclasses.dataclass being a class decorator rather than a metaclass-powered class is preventing __init_subclass__ from accessing fields of a dataclass in a response to @MegaIng, I set out to implement exactly that, a metaclass that is really a thin wrapper to the dataclass decorator with all of its capabilities while calling __init_subclass__ only after the new class has been transformed by dataclass.
The goal was largely achieved by:
- inserting a dummy base class with a no-op __init_subclass__to block any__init_subclass__of the rest of the base classes from being executed during the creation of the new class, and,
- deleting __init_subclass__of the dummy base class after the new class is dataclass-transformed, so that,
- calling __init_subclass__of the super class of the new class can then follow the intended MRO
from dataclasses import dataclass, fields
from typing import dataclass_transform
@dataclass_transform()
class DataclassMeta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        class InitSubclassBlocker:
            def __init_subclass__(cls, **kwargs):
                pass
        configured_dataclass = dataclass(**kwargs)
        for key in ('slots', 'frozen'): # TODO: pop all known dataclass keywords
            kwargs.pop(key, None)
        cls = configured_dataclass(super().__new__(
            metacls, name, (InitSubclassBlocker,) + bases, namespace, **kwargs))
        del InitSubclassBlocker.__init_subclass__
        super(cls, cls).__init_subclass__(**kwargs)
        return cls
class Dataclass(metaclass=DataclassMeta):
    pass
so that I can have a base class that processes dataclass fields when subclassed:
class PrintFields(Dataclass):
    def __init_subclass__(cls, **kwargs):
        for field in fields(cls):
            print(field.name, field.type)
        super().__init_subclass__(**kwargs)
class Foo(PrintFields):
    foo: str
which correctly outputs:
foo <class 'str'>
But then I tried enabling the slots option for PrintFields:
class PrintFields(Dataclass, slots=True):
    def __init_subclass__(cls, **kwargs):
        for field in fields(cls):
            print(field.name, field.type)
        super().__init_subclass__(**kwargs)
And got:
Traceback (most recent call last):
  File "/ATO/code", line 21, in <module>
    class PrintFields(Dataclass, slots=True):
TypeError: __class__ set to <class '__main__.PrintFields'> defining 'PrintFields' as <class '__main__.PrintFields'>
Demo here
I’ve never seen this error before, and a look at the CPython source that produces this error message tells me that it has something to do with __classcell__. With a bit of a search for that keyword, I found an open CPython issue (originated from this issue) regarding this symptom, but am still unable to comprehend from the responses the actual cause of this issue, and how I can possibly work around it.
Any insight would be appreciated.