gh-105936: Properly update closure cells for `__setattr__` and `__del… · python/cpython@8a398bf
@@ -3052,29 +3052,41 @@ class C(base):
305230523053305330543054class TestFrozen(unittest.TestCase):
3055+# Some tests have a subtest with a slotted dataclass.
3056+# See https://github.com/python/cpython/issues/105936 for the reasons.
3057+30553058def test_frozen(self):
3056-@dataclass(frozen=True)
3057-class C:
3058-i: int
3059+for slots in (False, True):
3060+with self.subTest(slots=slots):
305930613060-c = C(10)
3061-self.assertEqual(c.i, 10)
3062-with self.assertRaises(FrozenInstanceError):
3063-c.i = 5
3064-self.assertEqual(c.i, 10)
3062+@dataclass(frozen=True, slots=slots)
3063+class C:
3064+i: int
3065+3066+c = C(10)
3067+self.assertEqual(c.i, 10)
3068+with self.assertRaises(FrozenInstanceError):
3069+c.i = 5
3070+self.assertEqual(c.i, 10)
3071+with self.assertRaises(FrozenInstanceError):
3072+del c.i
3073+self.assertEqual(c.i, 10)
3065307430663075def test_frozen_empty(self):
3067-@dataclass(frozen=True)
3068-class C:
3069-pass
3076+for slots in (False, True):
3077+with self.subTest(slots=slots):
307030783071-c = C()
3072-self.assertNotHasAttr(c, 'i')
3073-with self.assertRaises(FrozenInstanceError):
3074-c.i = 5
3075-self.assertNotHasAttr(c, 'i')
3076-with self.assertRaises(FrozenInstanceError):
3077-del c.i
3079+@dataclass(frozen=True, slots=slots)
3080+class C:
3081+pass
3082+3083+c = C()
3084+self.assertNotHasAttr(c, 'i')
3085+with self.assertRaises(FrozenInstanceError):
3086+c.i = 5
3087+self.assertNotHasAttr(c, 'i')
3088+with self.assertRaises(FrozenInstanceError):
3089+del c.i
3078309030793091def test_inherit(self):
30803092@dataclass(frozen=True)
@@ -3270,41 +3282,43 @@ class D(I):
32703282d.i = 5
3271328332723284def test_non_frozen_normal_derived(self):
3273-# See bpo-32953.
3274-3275-@dataclass(frozen=True)
3276-class D:
3277-x: int
3278-y: int = 10
3279-3280-class S(D):
3281-pass
3285+# See bpo-32953 and https://github.com/python/cpython/issues/105936
3286+for slots in (False, True):
3287+with self.subTest(slots=slots):
328232883283-s = S(3)
3284-self.assertEqual(s.x, 3)
3285-self.assertEqual(s.y, 10)
3286-s.cached = True
3289+ @dataclass(frozen=True, slots=slots)
3290+ class D:
3291+ x: int
3292+ y: int = 10
328732933288-# But can't change the frozen attributes.
3289-with self.assertRaises(FrozenInstanceError):
3290-s.x = 5
3291-with self.assertRaises(FrozenInstanceError):
3292-s.y = 5
3293-self.assertEqual(s.x, 3)
3294-self.assertEqual(s.y, 10)
3295-self.assertEqual(s.cached, True)
3294+class S(D):
3295+pass
329632963297-with self.assertRaises(FrozenInstanceError):
3298-del s.x
3299-self.assertEqual(s.x, 3)
3300-with self.assertRaises(FrozenInstanceError):
3301-del s.y
3302-self.assertEqual(s.y, 10)
3303-del s.cached
3304-self.assertNotHasAttr(s, 'cached')
3305-with self.assertRaises(AttributeError) as cm:
3306-del s.cached
3307-self.assertNotIsInstance(cm.exception, FrozenInstanceError)
3297+s = S(3)
3298+self.assertEqual(s.x, 3)
3299+self.assertEqual(s.y, 10)
3300+s.cached = True
3301+3302+# But can't change the frozen attributes.
3303+with self.assertRaises(FrozenInstanceError):
3304+s.x = 5
3305+with self.assertRaises(FrozenInstanceError):
3306+s.y = 5
3307+self.assertEqual(s.x, 3)
3308+self.assertEqual(s.y, 10)
3309+self.assertEqual(s.cached, True)
3310+3311+with self.assertRaises(FrozenInstanceError):
3312+del s.x
3313+self.assertEqual(s.x, 3)
3314+with self.assertRaises(FrozenInstanceError):
3315+del s.y
3316+self.assertEqual(s.y, 10)
3317+del s.cached
3318+self.assertNotHasAttr(s, 'cached')
3319+with self.assertRaises(AttributeError) as cm:
3320+del s.cached
3321+self.assertNotIsInstance(cm.exception, FrozenInstanceError)
3308332233093323def test_non_frozen_normal_derived_from_empty_frozen(self):
33103324@dataclass(frozen=True)
@@ -3971,6 +3985,14 @@ class SlotsTest:
3971398539723986return SlotsTest
397339873988+# See https://github.com/python/cpython/issues/135228#issuecomment-3755979059
3989+def make_frozen():
3990+@dataclass(frozen=True, slots=True)
3991+class SlotsTest:
3992+pass
3993+3994+return SlotsTest
3995+39743996def make_with_annotations():
39753997@dataclass(slots=True)
39763998class SlotsTest:
@@ -3996,7 +4018,7 @@ class SlotsTest:
3996401839974019return SlotsTest
399840203999-for make in (make_simple, make_with_annotations, make_with_annotations_and_method, make_with_forwardref):
4021+for make in (make_simple, make_frozen, make_with_annotations, make_with_annotations_and_method, make_with_forwardref):
40004022with self.subTest(make=make):
40014023C = make()
40024024support.gc_collect()