Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dataclass, slots, __post_init__ and super #111500

Closed
voidspace opened this issue Oct 30, 2023 · 5 comments
Closed

dataclass, slots, __post_init__ and super #111500

voidspace opened this issue Oct 30, 2023 · 5 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@voidspace
Copy link
Contributor

voidspace commented Oct 30, 2023

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

@voidspace voidspace added the type-bug An unexpected behavior, bug, or error label Oct 30, 2023
@Eclips4 Eclips4 added the stdlib Python modules in the Lib dir label Oct 30, 2023
@sobolevn
Copy link
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! :)

@sobolevn sobolevn self-assigned this Oct 30, 2023
@sobolevn
Copy link
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

@voidspace
Copy link
Contributor Author

Nice work @sobolevn

@JelleZijlstra
Copy link
Member

Duplicate of #90562

@JelleZijlstra JelleZijlstra marked this as a duplicate of #90562 Mar 12, 2024
@JelleZijlstra JelleZijlstra closed this as not planned Won't fix, can't repro, duplicate, stale Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants