mirror of
https://github.com/python/cpython.git
synced 2026-04-26 13:50:54 +00:00
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:
parent
5ea3ae7c97
commit
6d7bbee1d5
3 changed files with 59 additions and 3 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue