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 <bartosz@ilikepython.com>
This commit is contained in:
Jelle Zijlstra 2026-04-25 08:31:22 -07:00 committed by GitHub
parent 5ea3ae7c97
commit 6d7bbee1d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 59 additions and 3 deletions

View file

@ -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

View file

@ -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()

View file

@ -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.