]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-150069: Test frozen dataclass and properties interaction (GH-150070) (...
authorsobolevn <mail@sobolevn.me>
Thu, 2 Jul 2026 13:21:39 +0000 (16:21 +0300)
committerGitHub <noreply@github.com>
Thu, 2 Jul 2026 13:21:39 +0000 (13:21 +0000)
(cherry picked from commit 753f82db2fed38bf725ded2b333ea3749c81fe82)

Lib/test/test_dataclasses/__init__.py

index ec3630f66b5a328cfa7362d59759610c522a523f..2af83a0ecdc15c0dfbee19e636a43f7247e0e8de 100644 (file)
@@ -3152,6 +3152,47 @@ class TestFrozen(unittest.TestCase):
                 class D:
                     x: int
                     y: int = 10
+                    z: int = 1
+
+                    @property
+                    def readonly(self) -> int:
+                        return self.x
+
+                    @property
+                    def prop(self) -> int:
+                        return self.z
+
+                    @prop.setter
+                    def prop(self, val: int) -> None:
+                        object.__setattr__(self, 'z', val)
+
+                    @prop.deleter
+                    def prop(self) -> None:
+                        object.__setattr__(self, 'z', 0)
+
+                d = D(5)
+                self.assertEqual(d.x, 5)
+                self.assertEqual(d.y, 10)
+                self.assertEqual(d.z, 1)
+                self.assertEqual(d.readonly, 5)
+                self.assertEqual(d.prop, 1)
+
+                with self.assertRaises(FrozenInstanceError):
+                    d.x = 5
+                with self.assertRaises(FrozenInstanceError):
+                    d.readonly = 5
+                with self.assertRaises(FrozenInstanceError):
+                    d.z = 5
+                with self.assertRaises(FrozenInstanceError):
+                    d.prop = 5
+                with self.assertRaises(FrozenInstanceError):
+                    del d.prop
+
+                self.assertEqual(d.x, 5)
+                self.assertEqual(d.y, 10)
+                self.assertEqual(d.z, 1)
+                self.assertEqual(d.readonly, 5)
+                self.assertEqual(d.prop, 1)
 
                 class S(D):
                     pass
@@ -3159,16 +3200,40 @@ class TestFrozen(unittest.TestCase):
                 s = S(3)
                 self.assertEqual(s.x, 3)
                 self.assertEqual(s.y, 10)
+                self.assertEqual(s.z, 1)
+                self.assertEqual(s.readonly, 3)
+                self.assertEqual(s.prop, 1)
+                # Can set new attrs:
                 s.cached = True
+                self.assertTrue(s.cached)
+                # Can mutate them:
+                s.cached = False
+                self.assertFalse(s.cached)
+
+                # Can also change writable properties:
+                with self.assertRaisesRegex(
+                    AttributeError,
+                    'object has no setter',
+                ) as cm:
+                    s.readonly = 5
+                self.assertNotIsInstance(cm.exception, FrozenInstanceError)
+                s.prop = 2
+                self.assertEqual(s.x, 3)
+                self.assertEqual(s.readonly, 3)
+                self.assertEqual(s.prop, 2)
+                self.assertEqual(s.z, 2)
 
                 # But can't change the frozen attributes.
                 with self.assertRaises(FrozenInstanceError):
                     s.x = 5
                 with self.assertRaises(FrozenInstanceError):
                     s.y = 5
+                with self.assertRaises(FrozenInstanceError):
+                    s.z = 5
                 self.assertEqual(s.x, 3)
                 self.assertEqual(s.y, 10)
-                self.assertEqual(s.cached, True)
+                self.assertEqual(s.z, 2)
+                self.assertIs(s.cached, False)
 
                 with self.assertRaises(FrozenInstanceError):
                     del s.x
@@ -3176,11 +3241,26 @@ class TestFrozen(unittest.TestCase):
                 with self.assertRaises(FrozenInstanceError):
                     del s.y
                 self.assertEqual(s.y, 10)
+                with self.assertRaisesRegex(
+                    AttributeError,
+                    'object has no deleter',
+                ) as cm:
+                    del s.readonly
+                self.assertNotIsInstance(cm.exception, FrozenInstanceError)
+                self.assertEqual(s.x, 3)
+                self.assertEqual(s.readonly, 3)
                 del s.cached
                 self.assertFalse(hasattr(s, 'cached'))
-                with self.assertRaises(AttributeError) as cm:
+                with self.assertRaisesRegex(
+                    AttributeError,
+                    "object has no attribute 'cached'",
+                ) as cm:
                     del s.cached
                 self.assertNotIsInstance(cm.exception, FrozenInstanceError)
+                del s.prop
+                self.assertEqual(s.z, 0)
+                self.assertEqual(s.prop, 0)
+                del s.prop
 
     def test_non_frozen_normal_derived_from_empty_frozen(self):
         @dataclass(frozen=True)