# How does "TypeError: \_\_class\_\_ set to \<class 'A'\> defining 'A' as \<class 'A'\>" occur?

**URL:** <https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354>\
**Category:** Python Help\
**Created:** [March 13, 2024, 8:15am UTC](https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354 "2024-03-13T08:15:46Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [March 13, 2024, 8:15am UTC](https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354/1 "2024-03-13T08:15:46Z")

</div>

Following my [rant](https://discuss.python.org/t/reduce-metaclass-conflicts-with-less-eager-proclamation-automatic-metaclass-merges/47739/18) 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:

1. 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,
2. deleting ` __init_subclass__ ` of the dummy base class after the new class is dataclass-transformed, so that,
3. calling ` __init_subclass__ ` of the super class of the new class can then follow the intended MRO

```python
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:

```python
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:

```python
foo <class 'str'>

```

But then I tried enabling the `slots` option for `PrintFields`:

```python
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:

```python
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](https://ato.pxeger.com/run?1=jVPNSsQwED5461Pk1nStPYuwILIIgoqg95DdTtewbRKSlGV9FS_rQR_KpzE_beN262IOIZl8M9_km5n3L7kzr4Lv95-tqS4uv88-KiUaVFJDVzXVGjRijRTKRFOOKgZ1qROPNDvJ-LoH3TMDitZ5RBOjKNeVUE2SXE9YcZZ4C1r0bw9gKLZhIbtKkF0lVIgQDltCcAMOZFPgtIEcLalNMFy0pCtrmc02W6rWuvN1K4S_48w8t0t_uanFagMqQiINszCiO5wl9GQTQfslLSwyCV6xdaugJMNP0TxqgYdAg4uVAG1ghxhHONW1MDrNUWqVfQOejviCbyGFxNYlR4-CQ_brm45rKgWsWwkKZ8UfKuIJbfIMnZ-SN_KWUE-JWxxpOXiEdHwCdsuOkRM6KTCt4g6fjPul-449zQ96qFPPV6jzeVKMm1vfvXgR-9nrPn9R7WHL_bcXXA39SLgqhtlw2FH1pOPG_rkIuoez7_SRNic1CZPaDWw_uD8)

I’ve never seen this error before, and a look at the CPython [source](https://github.com/python/cpython/blob/ba82a241ac7ddee7cad18e9994f8dd560c68df02/Python/bltinmodule.c#L221) 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](https://github.com/python/cpython/issues/91300) (originated from this [issue](https://bugs.python.org/issue47144)) 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.

---

<div class="post-metadata">

**Author:** ![TeamSpen210](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/teamspen210/32/1004_2.png) [@TeamSpen210](https://discuss.python.org/u/TeamSpen210)\
**Post date:** [March 13, 2024, 10:12am UTC](https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354/2 "2024-03-13T10:12:52Z")

</div>

` __classcell__ ` has to do with the internals of `super()`'s zero-argument feature. For that to work, it needs to fetch `self` and the class the method was defined in. `self` can be grabbed by looking through the caller’s local variables, but the defining class needs help. What happens is if the name `super` is referenced in a method, the compiler will add a closure variable named ` __class__ `, set to the class. Where ` __classcell__ ` comes into play is that when executing the class body, a new cell object is created and stored under that name. The methods being defined fetch that to put in their closures, then `type. __new__ ` sets it to the class after that’s actually created.

So the problem with using `slots` is that currently that has to be set at class creation. So dataclass has to go create a new class entirely. This means we ended up with two classes with different names - the undecorated one in the cell object, the decorated one returned from your metaclass. This error detected the mismatch and complained.

I think the solution is for your code to check for the ` __classcell__ ` in the namespace, and if present make sure you update the contents to be the final class object.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [March 13, 2024, 10:30am UTC](https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354/3 "2024-03-13T10:30:36Z")

</div>

Thanks for the detailed explanations of what ` __classcell__ ` does.

Actually after I posted the question I found a good solution to this issue on StackOverflow:

> <https://stackoverflow.com/questions/41343263/provide-classcell-example-for-python-3-6-metaclass/41347038#41347038>

> [@](#):
>
> An example of doing this correctly can be seen in a [pull request made to Django](https://github.com/django/django/pull/7653/files) that uses this to fix an issue that was introduced in Python 3.6:
> 
> ```python
> new_attrs = {' __module__': module}
> classcell = attrs.pop(' __classcell__', None)
> if classcell is not None:
> new_attrs[' __classcell__'] = classcell
> new_class = super_new(cls, name, bases, new_attrs)
> 
> ```
> 
> The ` __classcell__ ` is simply added to the new namespace before being passed to `type. __new__ `.

And knowing that the new class when `slots=True` is created by `dataclasses._add_slots`:

> <https://github.com/python/cpython/blob/8332e85b2f079e8b9334666084d1f8495cff25c1/Lib/dataclasses.py#L1215>

I simply use a closure to inject ` __classcell__ ` into the namespace:

```python
def inject_classcell(classcell):
    def _add_slots(cls, is_frozen, weakref_slot):
        ...
        # And finally create the class.
        qualname = getattr(cls, ' __qualname__', None)
        if classcell:
            cls_dict[' __classcell__'] = classcell
        cls = type(cls)(cls. __name__ , cls. __bases__ , cls_dict)
        if qualname is not None:
            cls. __qualname__ = qualname
        ...
    return _add_slots

```

and patch `dataclasses._add_slots` with the injected version:

```python
from unittest.mock import patch

@dataclass_transform()
class DataclassMeta(type):
    def __new__ (metacls, name, bases, namespace, **kwargs):
        ...
        cls = super(). __new__ (
            metacls, name, (InitSubclassBlocker,) + bases, namespace, **kwargs)
        with patch('dataclasses._add_slots',
                inject_classcell(namespace.get(' __classcell__'))):
            cls = configured_dataclass(cls)
        del InitSubclassBlocker. __init_subclass__
        super(cls, cls). __init_subclass__ (**kwargs)
        return cls

```

And the code would work.

Demo [here](https://ato.pxeger.com/run?1=jVfNjuQ0EL63eAhr95Bk6Yk0ICQYaaQd2F0JoVmhYW6jleVOKt2m3XawnRn1Ip6Ey17gaXgCnoaynTjOzwA5zMROueqrr76y3b__2Z7tQclPn_7obHPx9d-f_cVPrdKWcAvaKiXMptHqRGpmWSWYMWBIbxGntqThIGr8T_dgqRHKuvf43c0ayyxMJk0_Gfzbc8vlfuGaWs2kaZQ-BbNOcmvB2PKkquNg3TJbHTabGhrC5c9QWerXViBEHt-Kqw3BxxlRVtcBJX5GpNxQdP4R5JY8ATtqaPzXfoV7XpL3ADWxilQaEDNhRMIT6bM3XFaAS0nFZGYJJkZo8E9p4oKwBjnFpX4ZOTBDdgCyd1mXm8T2lh2BmE7D6AlhOu9MoHl9dlHKuIA3JIuGGbKAMUxJac2RDDrm4R7NuAFyf27hrdZK5032azCW7ASU_jZGaKHiWFkzYsiKFOR3KRcuFMFCEdXpkZwRIsbwcMi1N81TgEW08kLySAwa2q4VkDelG3vfrsC92JyDolilzIMltXJ0qUfQgrXkidsDQaVj6uhix_B_L-eERXkAjbrvxYEAkOR8Ql7sirI6MC5LJ0rqJtkOkZ5Ym6ctEHI8aUXpw-XVxeWHBHAK_f4wgB4IDOQRcgcnzGD46DrAfZ3AJ-SmrhNnKIRBxU4KzUTT5AlVt-ePTuudFIA65NjrJha91WBA2mXdHl5EFbz4EGvzDDkNF_jeMGFmJis0I0OVkhbZRNfbpfWU8aW7kPX-cPHNl199cXk1yx8hKJQj1MZ17w5l5KFhM6sOE2-ecTejw2lmBnt1YSLfLcknSLbFohaA9JC8WHiazhTbRDNjo7huiNFiW4TY04Z_OajISYdZq_musw4g4hnK7SR4zoQgxnL8u4OZB8IeGRdO5C4Uvb25--HtXTkxGoRStqrNRzBb8l5JmOwbPZyh-7HEBkSzlJz3lA1m2YqnG1lj3pIJcR52ZnuA-dbzS8eEZ-maYHM6BsK-j66HT4n7YRWyEw-Pq9VMH9BBNEEPri3iOM3GtQvut37LytPNdtgiXEObfuhdT2BE_NinEnXjcC4QlWkyGHAYbFJP8aSbKyQcbwcedqCWV0fhjuNgPGw1YRMNTbsSfjjlffiV039liXlmSbwbxDUabKf9sbZJhuNJvtm8Xrk0YHOFw_bN8O0WBZC7YqT3AYrnFaX5CZwRCiPo1hclDEzLKpx59er4xPTeJDeD4P57vJb81O384FuBVxPQU4ZDGI5m1PR2GNAHW3E6PC2ajZGUbPgez7d6JModpsN7Hh1NNokjnF3L5pmnCVWehaJms3hhrW85XDJvhiBi07Wg86J8hrB8hQbc9j7_NyZjBK8tf5PLs-SyWY4lRuyL6110WaLK8lk_FkWxaBPXoitE-taMtjWItZKWiwrGFYEZz4XztLRcqU4i6blKe2bx7Xqi3D4fr4t-zY-aS_suXInejDdyz9j1ve6mQv-_CozHy-zCNeWzdbHDXl8GCYR3318zbv6bk9d-GjM_qDpCdpf9WeQ0-3dK5QkDvVWj1BWeZHoTftT0v22G3zj_AA)

Obviously patching `dataclasses._add_slot` with copied code like that is super ugly and unmaintainable, but I don’t see a better solution short of asking for an enhancement to `dataclasses._add_slot` in the Ideas forum.

Suggestions are welcome.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [March 13, 2024, 3:22pm UTC](https://discuss.python.org/t/how-does-typeerror-class-set-to-class-a-defining-a-as-class-a-occur/48354/4 "2024-03-13T15:22:50Z")

</div>

Since `dataclass._add_slots` gets its namespace from the ` __dict__ ` of the current class:

> <https://github.com/python/cpython/blob/8332e85b2f079e8b9334666084d1f8495cff25c1/Lib/dataclasses.py#L1183>

I solved the problem by propagating ` __classcell__ ` as a class attribute for `dataclass` to create a namespace with, and deleting it afterwards:

```python
_dataclass_keywords = set((code := dataclass. __code__ ).co_varnames[
    code.co_argcount:code.co_argcount + code.co_kwonlyargcount])

@dataclass_transform()
class DataclassMeta(type):
    def __new__ (metacls, name, bases, namespace, **kwargs):
        class InitSubclassBlocker:
            def __init_subclass__ (cls, **kwargs):
                pass
        configured_dataclass = dataclass(**{key: kwargs.pop(key)
            for key in kwargs.keys() & _dataclass_keywords})
        cls = super(). __new__ (
            metacls, name, (InitSubclassBlocker,) + bases, namespace, **kwargs)
        if classcell := namespace.get(' __classcell__'):
            cls. __classcell__ = classcell
        cls = configured_dataclass(cls)
        if hasattr(cls, ' __classcell__'):
            del cls. __classcell__
        del InitSubclassBlocker. __init_subclass__
        super(cls, cls). __init_subclass__ (**kwargs)
        return cls

```

Demo [here](https://ato.pxeger.com/run?1=jVTNatwwED71oqfQqZE2G5-LYaGUEOih0NDeShGKLW_E2pKQ5C5L6ZP0kkv7UH2ajiT_2yzxyZr5NN_MN6P5_ddc_LNWLy9_Wl_dvfv35rGyusEl97youXPCYdkYbf1o2uNKirp0KCL9xUh1XIGYt1y5StsGITZaT-Jy1rZ0-ICd8IQUuhQ4P4z3MsaCjTGaFZr94FbxRrhvCMMXHMHK7bHQrfL50oBvB8zprFV96R3fKULvN3IjFEULvu99n4TnBGoSNI-cpagwY0qcGSONCCCoP-S0x08c1EkHZ3gBlt3udAZK192NOcfwH5X0X9qnePhQ6-Ik7AgZaSTAmOtwQBjJNoL2nwHYyKRVJY-tFeWoN54oS3a7nyB_jlO4zGhD4ExnUUEUDEYsVQ-DkyMUv8UbXfxFJ3XGnrZGWEKzXrFZ7IV8ZEOUPYUWXtF1iCerpGwh6jrMzwDOjjBWNzBEvZexm4VykEM2A0Diw2lR0JaooS2zTJ65497b1K7r3KWo1_xo6t1QJVtNxnAjCR6JQ1ZrJFlrZ4VvrQp4tJz-bsTh7zB7EV0Vcd66O5-tVP4hLgJyP64GV2vvDl9tO39Ar53sMH9xu4QJTGsmyj1X0QRuEt1ZGqb0H9_tQpvrmnTFPGhNJgVtlFFpnWPnLUKJfIIGggiHndU5IdzEmDZrt2D7Rfsf)
