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