Skip to content

Commit 121ab58

Browse files
GH-93521: For dataclasses, filter out __weakref__ slot if present in bases (GH-93535)
(cherry picked from commit 5849af7) Co-authored-by: Bluenix <bluenixdev@gmail.com>
1 parent f26d1b5 commit 121ab58

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

Lib/dataclasses.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,11 +1156,16 @@ def _add_slots(cls, is_frozen, weakref_slot):
11561156
itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1]))
11571157
)
11581158
# The slots for our class. Remove slots from our base classes. Add
1159-
# '__weakref__' if weakref_slot was given.
1159+
# '__weakref__' if weakref_slot was given, unless it is already present.
11601160
cls_dict["__slots__"] = tuple(
1161-
itertools.chain(
1162-
itertools.filterfalse(inherited_slots.__contains__, field_names),
1163-
("__weakref__",) if weakref_slot else ())
1161+
itertools.filterfalse(
1162+
inherited_slots.__contains__,
1163+
itertools.chain(
1164+
# gh-93521: '__weakref__' also needs to be filtered out if
1165+
# already present in inherited_slots
1166+
field_names, ('__weakref__',) if weakref_slot else ()
1167+
)
1168+
),
11641169
)
11651170

11661171
for field_name in field_names:

Lib/test/test_dataclasses.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3109,6 +3109,54 @@ def test_weakref_slot_make_dataclass(self):
31093109
"weakref_slot is True but slots is False"):
31103110
B = make_dataclass('B', [('a', int),], weakref_slot=True)
31113111

3112+
def test_weakref_slot_subclass_weakref_slot(self):
3113+
@dataclass(slots=True, weakref_slot=True)
3114+
class Base:
3115+
field: int
3116+
3117+
# A *can* also specify weakref_slot=True if it wants to (gh-93521)
3118+
@dataclass(slots=True, weakref_slot=True)
3119+
class A(Base):
3120+
...
3121+
3122+
# __weakref__ is in the base class, not A. But an instance of A
3123+
# is still weakref-able.
3124+
self.assertIn("__weakref__", Base.__slots__)
3125+
self.assertNotIn("__weakref__", A.__slots__)
3126+
a = A(1)
3127+
weakref.ref(a)
3128+
3129+
def test_weakref_slot_subclass_no_weakref_slot(self):
3130+
@dataclass(slots=True, weakref_slot=True)
3131+
class Base:
3132+
field: int
3133+
3134+
@dataclass(slots=True)
3135+
class A(Base):
3136+
...
3137+
3138+
# __weakref__ is in the base class, not A. Even though A doesn't
3139+
# specify weakref_slot, it should still be weakref-able.
3140+
self.assertIn("__weakref__", Base.__slots__)
3141+
self.assertNotIn("__weakref__", A.__slots__)
3142+
a = A(1)
3143+
weakref.ref(a)
3144+
3145+
def test_weakref_slot_normal_base_weakref_slot(self):
3146+
class Base:
3147+
__slots__ = ('__weakref__',)
3148+
3149+
@dataclass(slots=True, weakref_slot=True)
3150+
class A(Base):
3151+
field: int
3152+
3153+
# __weakref__ is in the base class, not A. But an instance of
3154+
# A is still weakref-able.
3155+
self.assertIn("__weakref__", Base.__slots__)
3156+
self.assertNotIn("__weakref__", A.__slots__)
3157+
a = A(1)
3158+
weakref.ref(a)
3159+
31123160

31133161
class TestDescriptors(unittest.TestCase):
31143162
def test_set_name(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed a case where dataclasses would try to add ``__weakref__`` into the
2+
``__slots__`` for a dataclass that specified ``weakref_slot=True`` when it was
3+
already defined in one of its bases. This resulted in a ``TypeError`` upon the
4+
new class being created.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy