https://github.com/python/cpython/commit/6d7bbee1d5714a345dca5a7e4089de3c2fc0fb59
commit: 6d7bbee1d5714a345dca5a7e4089de3c2fc0fb59
branch: main
author: Jelle Zijlstra <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-04-25T08:31:22-07:00
summary:

gh-148947: dataclasses: fix error on empty __class__ cell  (#148948)

Also add a test demonstrating the need for the existing "is oldcls" check.

Co-authored-by: Bartosz SÅ‚awecki <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-04-23-21-47-49.gh-issue-148947.W4V2lG.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses/__init__.py

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 9d5bed6b96fc49..988edfed6f4dcb 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1298,10 +1298,18 @@ def _update_func_cell_for__class__(f, oldcls, newcls):
         # This function doesn't reference __class__, so nothing to do.
         return False
     # Fix the cell to point to the new class, if it's already pointing
-    # at the old class.  I'm not convinced that the "is oldcls" test
-    # is needed, but other than performance can't hurt.
+    # at the old class.
     closure = f.__closure__[idx]
-    if closure.cell_contents is oldcls:
+
+    try:
+        contents = closure.cell_contents
+    except ValueError:
+        # Cell is empty
+        return False
+
+    # This check makes it so we avoid updating an incorrect cell if the
+    # class body contains a function that was defined in a different class.
+    if contents is oldcls:
         closure.cell_contents = newcls
         return True
     return False
diff --git a/Lib/test/test_dataclasses/__init__.py 
b/Lib/test/test_dataclasses/__init__.py
index e0cfe3df3e6357..6ff82b8810abed 100644
--- a/Lib/test/test_dataclasses/__init__.py
+++ b/Lib/test/test_dataclasses/__init__.py
@@ -5375,5 +5375,51 @@ def cls(self):
         # one will be keeping a reference to the underlying class A.
         self.assertIs(A().cls(), B)
 
+    def test_empty_class_cell(self):
+        # gh-148947: Make sure that we explicitly handle the empty class cell.
+        def maker():
+            if False:
+                __class__ = 42
+
+            def method(self):
+                return __class__
+            return method
+
+        from dataclasses import dataclass
+
+        @dataclass(slots=True)
+        class X:
+            a: int
+
+            meth = maker()
+
+        with self.assertRaisesRegex(NameError, '__class__'):
+            X(1).meth()
+
+    def test_class_cell_from_other_class(self):
+        # This test fails without the "is oldcls" check in
+        # _update_func_cell_for__class__.
+        class Base:
+            def meth(self):
+                return "Base"
+
+        class Child(Base):
+            def meth(self):
+                return super().meth() + " Child"
+
+        @dataclass(slots=True)
+        class DC(Child):
+            a: int
+
+            meth = Child.meth
+
+        closure = DC.meth.__closure__
+        self.assertEqual(len(closure), 1)
+        self.assertIs(closure[0].cell_contents, Child)
+
+        self.assertEqual(DC(1).meth(), "Base Child")
+
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Library/2026-04-23-21-47-49.gh-issue-148947.W4V2lG.rst 
b/Misc/NEWS.d/next/Library/2026-04-23-21-47-49.gh-issue-148947.W4V2lG.rst
new file mode 100644
index 00000000000000..f9783266f5cc42
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-04-23-21-47-49.gh-issue-148947.W4V2lG.rst
@@ -0,0 +1,2 @@
+Fix crash in :deco:`dataclasses.dataclass` with ``slots=True`` that occurred
+when a function found within the class had an empty ``__class__`` cell.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to