Skip to content

Commit 7c3d881

Browse files
committed
pythongh-118033: Fix __weakref__ not set for generic dataclasses
1 parent 4605a19 commit 7c3d881

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

Lib/dataclasses.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,10 @@ def _get_slots(cls):
12011201
match cls.__dict__.get('__slots__'):
12021202
# A class which does not define __slots__ at all is equivalent
12031203
# to a class defining __slots__ = ('__dict__', '__weakref__')
1204+
case None if getattr(cls, '__weakrefoffset__', -1) == 0:
1205+
# Except for special cases, inheriting from them do not set
1206+
# any slots at all:
1207+
yield from ()
12041208
case None:
12051209
yield from ('__dict__', '__weakref__')
12061210
case str(slot):
@@ -1228,6 +1232,7 @@ def _add_slots(cls, is_frozen, weakref_slot):
12281232
inherited_slots = set(
12291233
itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1]))
12301234
)
1235+
print(inherited_slots)
12311236
# The slots for our class. Remove slots from our base classes. Add
12321237
# '__weakref__' if weakref_slot was given, unless it is already present.
12331238
cls_dict["__slots__"] = tuple(

Lib/test/test_dataclasses/__init__.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3515,8 +3515,103 @@ class A:
35153515
class B(A):
35163516
pass
35173517

3518+
self.assertEqual(B.__slots__, ())
35183519
B()
35193520

3521+
def test_dataclass_derived_generic(self):
3522+
T = typing.TypeVar('T')
3523+
3524+
@dataclass(slots=True, weakref_slot=True)
3525+
class A(typing.Generic[T]):
3526+
pass
3527+
self.assertEqual(A.__slots__, ('__weakref__',))
3528+
self.assertTrue(A.__weakref__)
3529+
A()
3530+
3531+
@dataclass(slots=True, weakref_slot=True)
3532+
class B[T2]:
3533+
pass
3534+
self.assertEqual(B.__slots__, ('__weakref__',))
3535+
self.assertTrue(B.__weakref__)
3536+
B()
3537+
3538+
def test_dataclass_derived_generic_from_base(self):
3539+
T = typing.TypeVar('T')
3540+
3541+
class RawBase: ...
3542+
3543+
@dataclass(slots=True, weakref_slot=True)
3544+
class C1(typing.Generic[T], RawBase):
3545+
pass
3546+
self.assertEqual(C1.__slots__, ())
3547+
self.assertTrue(C1.__weakref__)
3548+
C1()
3549+
@dataclass(slots=True, weakref_slot=True)
3550+
class C2(RawBase, typing.Generic[T]):
3551+
pass
3552+
self.assertEqual(C2.__slots__, ())
3553+
self.assertTrue(C2.__weakref__)
3554+
C2()
3555+
3556+
@dataclass(slots=True, weakref_slot=True)
3557+
class D[T2](RawBase):
3558+
pass
3559+
self.assertEqual(D.__slots__, ())
3560+
self.assertTrue(D.__weakref__)
3561+
D()
3562+
3563+
def test_dataclass_derived_generic_from_slotted_base(self):
3564+
T = typing.TypeVar('T')
3565+
3566+
class WithSlots:
3567+
__slots__ = ('a', 'b')
3568+
3569+
@dataclass(slots=True, weakref_slot=True)
3570+
class E1(WithSlots, Generic[T]):
3571+
pass
3572+
self.assertEqual(E1.__slots__, ('__weakref__',))
3573+
self.assertTrue(E1.__weakref__)
3574+
E1()
3575+
@dataclass(slots=True, weakref_slot=True)
3576+
class E2(Generic[T], WithSlots):
3577+
pass
3578+
self.assertEqual(E2.__slots__, ('__weakref__',))
3579+
self.assertTrue(E2.__weakref__)
3580+
E2()
3581+
3582+
@dataclass(slots=True, weakref_slot=True)
3583+
class F[T2](WithSlots):
3584+
pass
3585+
self.assertEqual(F.__slots__, ('__weakref__',))
3586+
self.assertTrue(F.__weakref__)
3587+
F()
3588+
3589+
def test_dataclass_derived_generic_from_slotted_base(self):
3590+
T = typing.TypeVar('T')
3591+
3592+
class WithWeakrefSlot:
3593+
__slots__ = ('__weakref__',)
3594+
3595+
@dataclass(slots=True, weakref_slot=True)
3596+
class G1(WithWeakrefSlot, Generic[T]):
3597+
pass
3598+
self.assertEqual(G1.__slots__, ())
3599+
self.assertTrue(G1.__weakref__)
3600+
G1()
3601+
@dataclass(slots=True, weakref_slot=True)
3602+
class G2(Generic[T], WithWeakrefSlot):
3603+
pass
3604+
self.assertEqual(G2.__slots__, ())
3605+
self.assertTrue(G2.__weakref__)
3606+
G2()
3607+
3608+
@dataclass(slots=True, weakref_slot=True)
3609+
class H[T2](WithWeakrefSlot):
3610+
pass
3611+
self.assertEqual(H.__slots__, ())
3612+
self.assertTrue(H.__weakref__)
3613+
H()
3614+
35203615

35213616
class TestDescriptors(unittest.TestCase):
35223617
def test_set_name(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`dataclasses.dataclass` not creating a ``__weakref__`` slot when
2+
subclassing :class:`typing.Generic`.

0 commit comments

Comments
 (0)