Skip to content

dataclass, slots, __post_init__ and super #111500

Closed as not planned
Closed as not planned

Description

@voidspace

Bug report

Bug description:

Using super() in __post_init__ on a dataclass with slots fails.

This code:

from dataclasses import dataclass

@dataclass(slots=True)
class Base:
    def __post_init__(self):
        pass

@dataclass(slots=True)
class Thing(Base):
    a: int
    b: int
    c: int = 0

    def __post_init__(self):
        self.c = self.a + self.b
        super().__post_init__()

t = Thing(1,3)

Produces this exception:

Traceback (most recent call last):
  File "C:\Users\michael.foord\Code\one-touch-switch-service\example.py", line 18, in <module>
    t = Thing(1,3)
        ^^^^^^^^^^
  File "<string>", line 6, in __init__
  File "C:\Users\michael.foord\Code\one-touch-switch-service\example.py", line 16, in __post_init__
    super().__post_init__()
    ^^^^^^^
TypeError: super(type, obj): obj must be an instance or subtype of type

The fix is to use Python 2 style super(Class, self).__post_init__().

CPython versions tested on:

3.11

Operating systems tested on:

Windows

Linked PRs

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Oct 30, 2023
  2. sobolevn commented on Oct 30, 2023

    @sobolevn
    Member

    I think that I found why this happens :)

    First of all, I was not able to reproduce it without @dataclass, so I assumed it is related to the internal machinery.

    This is my debug patch:

    » git patch
    diff --git Lib/dataclasses.py Lib/dataclasses.py
    index 2fba32b5ffb..a7ce3c03e9b 100644
    --- Lib/dataclasses.py
    +++ Lib/dataclasses.py
    @@ -1232,7 +1232,7 @@ def _add_slots(cls, is_frozen, weakref_slot):
     
         # And finally create the class.
         qualname = getattr(cls, '__qualname__', None)
    -    cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
    +    cls = type(cls)(cls.__name__ + '__slots', cls.__bases__, cls_dict)
         if qualname is not None:
             cls.__qualname__ = qualname
     
    diff --git Objects/typeobject.c Objects/typeobject.c
    index 25085693070..79db15579e5 100644
    --- Objects/typeobject.c
    +++ Objects/typeobject.c
    @@ -10400,9 +10400,11 @@ supercheck(PyTypeObject *type, PyObject *obj)
             Py_XDECREF(class_attr);
         }
     
    -    PyErr_SetString(PyExc_TypeError,
    +    PyErr_Format(PyExc_TypeError,
                         "super(type, obj): "
    -                    "obj must be an instance or subtype of type");
    +                    "obj must be an instance or subtype of type %s %s",
    +                    Py_TYPE(obj)->tp_name,
    +                    type->tp_name);
         return NULL;
     }
     

    Turns out, I was right. Given the code in the original bug report, it produces:

    Traceback (most recent call last):
      File "/Users/sobolev/Desktop/cpython2/ex.py", line 32, in <module>
        Thing2(1, 2)
      File "/Users/sobolev/Desktop/cpython2/ex.py", line 19, in __init__
        self.__post_init__()
      File "/Users/sobolev/Desktop/cpython2/ex.py", line 24, in __post_init__
        super().__post_init__()
        ^^^^^^^^^^^^^^^^^^^^^
    TypeError: super(type, obj): obj must be an instance or subtype of type Thing__slots Thing
    

    Looks like we add slots and regenerate class in the very end. I think we can try doing it at the beggining. Maybe it will help.

    I am working on this idea! :)

  3. self-assigned this
    on Oct 30, 2023
  4. sobolevn commented on Oct 31, 2023

    @sobolevn
    Member

    Here are some experiments that I had:

    from dataclasses import dataclass
    # from attr import dataclass
    
    @dataclass(slots=True)
    class Base:
        def m(self):
            return "DONE!"
    
    class Thing(Base):
        def m(self):
            print(super().m())
    
    Thing2 = dataclass(slots=True)(Thing)
    Thing2().m()

    It fails with the same error. However, if you pip install attrs and uncomment # from attr import dataclass it will work and print DONE!

    So, it can be fixed (given that our _add_slots functions are similar): https://github.com/python-attrs/attrs/blob/52c1cd97d723cde6d75259a22d218f9305ecf8ee/src/attr/_make.py#L809-L914

  5. added a commit that references this issue on Oct 31, 2023
  6. voidspace commented on Nov 6, 2023

    @voidspace
    ContributorAuthor

    Nice work @sobolevn

  7. JelleZijlstra commented on Mar 12, 2024

    @JelleZijlstra
    Member

    Duplicate of #90562

  8. added a commit that references this issue on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions