mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
GH-93521: For dataclasses, filter out __weakref__
slot if present in bases (GH-93535)
(cherry picked from commit 5849af7a80
)
Co-authored-by: Bluenix <bluenixdev@gmail.com>
This commit is contained in:
parent
f26d1b5b53
commit
121ab58e03
3 changed files with 61 additions and 4 deletions
|
@ -1156,11 +1156,16 @@ def _add_slots(cls, is_frozen, weakref_slot):
|
||||||
itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1]))
|
itertools.chain.from_iterable(map(_get_slots, cls.__mro__[1:-1]))
|
||||||
)
|
)
|
||||||
# The slots for our class. Remove slots from our base classes. Add
|
# The slots for our class. Remove slots from our base classes. Add
|
||||||
# '__weakref__' if weakref_slot was given.
|
# '__weakref__' if weakref_slot was given, unless it is already present.
|
||||||
cls_dict["__slots__"] = tuple(
|
cls_dict["__slots__"] = tuple(
|
||||||
itertools.chain(
|
itertools.filterfalse(
|
||||||
itertools.filterfalse(inherited_slots.__contains__, field_names),
|
inherited_slots.__contains__,
|
||||||
("__weakref__",) if weakref_slot else ())
|
itertools.chain(
|
||||||
|
# gh-93521: '__weakref__' also needs to be filtered out if
|
||||||
|
# already present in inherited_slots
|
||||||
|
field_names, ('__weakref__',) if weakref_slot else ()
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
for field_name in field_names:
|
for field_name in field_names:
|
||||||
|
|
|
@ -3109,6 +3109,54 @@ class TestSlots(unittest.TestCase):
|
||||||
"weakref_slot is True but slots is False"):
|
"weakref_slot is True but slots is False"):
|
||||||
B = make_dataclass('B', [('a', int),], weakref_slot=True)
|
B = make_dataclass('B', [('a', int),], weakref_slot=True)
|
||||||
|
|
||||||
|
def test_weakref_slot_subclass_weakref_slot(self):
|
||||||
|
@dataclass(slots=True, weakref_slot=True)
|
||||||
|
class Base:
|
||||||
|
field: int
|
||||||
|
|
||||||
|
# A *can* also specify weakref_slot=True if it wants to (gh-93521)
|
||||||
|
@dataclass(slots=True, weakref_slot=True)
|
||||||
|
class A(Base):
|
||||||
|
...
|
||||||
|
|
||||||
|
# __weakref__ is in the base class, not A. But an instance of A
|
||||||
|
# is still weakref-able.
|
||||||
|
self.assertIn("__weakref__", Base.__slots__)
|
||||||
|
self.assertNotIn("__weakref__", A.__slots__)
|
||||||
|
a = A(1)
|
||||||
|
weakref.ref(a)
|
||||||
|
|
||||||
|
def test_weakref_slot_subclass_no_weakref_slot(self):
|
||||||
|
@dataclass(slots=True, weakref_slot=True)
|
||||||
|
class Base:
|
||||||
|
field: int
|
||||||
|
|
||||||
|
@dataclass(slots=True)
|
||||||
|
class A(Base):
|
||||||
|
...
|
||||||
|
|
||||||
|
# __weakref__ is in the base class, not A. Even though A doesn't
|
||||||
|
# specify weakref_slot, it should still be weakref-able.
|
||||||
|
self.assertIn("__weakref__", Base.__slots__)
|
||||||
|
self.assertNotIn("__weakref__", A.__slots__)
|
||||||
|
a = A(1)
|
||||||
|
weakref.ref(a)
|
||||||
|
|
||||||
|
def test_weakref_slot_normal_base_weakref_slot(self):
|
||||||
|
class Base:
|
||||||
|
__slots__ = ('__weakref__',)
|
||||||
|
|
||||||
|
@dataclass(slots=True, weakref_slot=True)
|
||||||
|
class A(Base):
|
||||||
|
field: int
|
||||||
|
|
||||||
|
# __weakref__ is in the base class, not A. But an instance of
|
||||||
|
# A is still weakref-able.
|
||||||
|
self.assertIn("__weakref__", Base.__slots__)
|
||||||
|
self.assertNotIn("__weakref__", A.__slots__)
|
||||||
|
a = A(1)
|
||||||
|
weakref.ref(a)
|
||||||
|
|
||||||
|
|
||||||
class TestDescriptors(unittest.TestCase):
|
class TestDescriptors(unittest.TestCase):
|
||||||
def test_set_name(self):
|
def test_set_name(self):
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Fixed a case where dataclasses would try to add ``__weakref__`` into the
|
||||||
|
``__slots__`` for a dataclass that specified ``weakref_slot=True`` when it was
|
||||||
|
already defined in one of its bases. This resulted in a ``TypeError`` upon the
|
||||||
|
new class being created.
|
Loading…
Add table
Add a link
Reference in a new issue