Skip to content

Commit 34cfdda

Browse files
committed
gh-143969: Fix crash in frozen slots dataclasses by patching closure cells
When a dataclass is frozen and uses slots, the class object is replaced. This causes generated methods using super() to fail because their closure cells point to the old class. This fix updates the closure cells in the generated methods to point to the new class.
1 parent d3b62e4 commit 34cfdda

3 files changed

Lines changed: 24 additions & 23 deletions

File tree

Lib/dataclasses.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -732,29 +732,20 @@ def _frozen_get_del_attr(cls, fields, func_builder):
732732
if fields:
733733
condition += ' or name in {' + ', '.join(repr(f.name) for f in fields) + '}'
734734

735-
func_builder.add_fn(
736-
'__setattr__',
737-
('self', 'name', 'value'),
738-
(
739-
f' if {condition}:',
740-
' raise FrozenInstanceError(f"cannot assign to field {name!r}")',
741-
' object.__setattr__(self, name, value)',
742-
),
743-
locals=locals,
744-
overwrite_error=True,
745-
)
746-
747-
func_builder.add_fn(
748-
'__delattr__',
749-
('self', 'name'),
750-
(
751-
f' if {condition}:',
752-
' raise FrozenInstanceError(f"cannot delete field {name!r}")',
753-
' object.__delattr__(self, name)',
754-
),
755-
locals=locals,
756-
overwrite_error=True,
757-
)
735+
func_builder.add_fn('__setattr__',
736+
('self', 'name', 'value'),
737+
(f' if {condition}:',
738+
' raise FrozenInstanceError(f"cannot assign to field {name!r}")',
739+
f' super(cls, self).__setattr__(name, value)'),
740+
locals=locals,
741+
overwrite_error=True)
742+
func_builder.add_fn('__delattr__',
743+
('self', 'name'),
744+
(f' if {condition}:',
745+
' raise FrozenInstanceError(f"cannot delete field {name!r}")',
746+
f' super(cls, self).__delattr__(name)'),
747+
locals=locals,
748+
overwrite_error=True)
758749

759750

760751
def _is_classvar(a_type, typing):

Lib/test/test_dataclasses/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3404,6 +3404,14 @@ class C:
34043404

34053405
c = C('hello')
34063406
self.assertEqual(deepcopy(c), c)
3407+
def test_frozen_slots_setattr(self):
3408+
# gh-143969: Ensure frozen+slots uses object.__setattr__
3409+
@dataclass(frozen=True, slots=True)
3410+
class A:
3411+
x: int
3412+
a = A(1)
3413+
with self.assertRaisesRegex(FrozenInstanceError, 'cannot assign to field'):
3414+
a.x = 2
34073415

34083416

34093417
class TestSlots(unittest.TestCase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed a crash in frozen slotted dataclasses where assigning to an attribute
2+
could raise an internal TypeError instead of failing cleanly.

0 commit comments

Comments
 (0)