Skip to content

Commit 6e1b31b

Browse files
miss-islingtonJelleZijlstrancoghlan
authored
[3.14] gh-135228: When @DataClass(slots=True) replaces a dataclass, make the original class collectible (GH-136893) (#136960)
gh-135228: When @DataClass(slots=True) replaces a dataclass, make the original class collectible (GH-136893) An interesting hack, but more localized in scope than GH-135230. This may be a breaking change if people intentionally keep the original class around when using `@dataclass(slots=True)`, and then use `__dict__` or `__weakref__` on the original class. (cherry picked from commit 46cbdf9) Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Alyssa Coghlan <ncoghlan@gmail.com>
1 parent caef946 commit 6e1b31b

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

Lib/dataclasses.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,13 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
13381338
or _update_func_cell_for__class__(member.fdel, cls, newcls)):
13391339
break
13401340

1341+
# gh-135228: Make sure the original class can be garbage collected.
1342+
# Bypass mapping proxy to allow __dict__ to be removed
1343+
old_cls_dict = cls.__dict__ | _deproxier
1344+
old_cls_dict.pop('__dict__', None)
1345+
if "__weakref__" in cls.__dict__:
1346+
del cls.__weakref__
1347+
13411348
return newcls
13421349

13431350

@@ -1732,3 +1739,11 @@ def _replace(self, /, **changes):
17321739
# changes that aren't fields, this will correctly raise a
17331740
# TypeError.
17341741
return self.__class__(**changes)
1742+
1743+
1744+
# Hack to the get the underlying dict out of a mappingproxy
1745+
# Use it with: cls.__dict__ | _deproxier
1746+
class _Deproxier:
1747+
def __ror__(self, other):
1748+
return other
1749+
_deproxier = _Deproxier()

Lib/test/test_dataclasses/__init__.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3804,6 +3804,41 @@ class WithCorrectSuper(CorrectSuper):
38043804
# that we create internally.
38053805
self.assertEqual(CorrectSuper.args, ["default", "default"])
38063806

3807+
def test_original_class_is_gced(self):
3808+
# gh-135228: Make sure when we replace the class with slots=True, the original class
3809+
# gets garbage collected.
3810+
def make_simple():
3811+
@dataclass(slots=True)
3812+
class SlotsTest:
3813+
pass
3814+
3815+
return SlotsTest
3816+
3817+
def make_with_annotations():
3818+
@dataclass(slots=True)
3819+
class SlotsTest:
3820+
x: int
3821+
3822+
return SlotsTest
3823+
3824+
def make_with_annotations_and_method():
3825+
@dataclass(slots=True)
3826+
class SlotsTest:
3827+
x: int
3828+
3829+
def method(self) -> int:
3830+
return self.x
3831+
3832+
return SlotsTest
3833+
3834+
for make in (make_simple, make_with_annotations, make_with_annotations_and_method):
3835+
with self.subTest(make=make):
3836+
C = make()
3837+
support.gc_collect()
3838+
candidates = [cls for cls in object.__subclasses__() if cls.__name__ == 'SlotsTest'
3839+
and cls.__firstlineno__ == make.__code__.co_firstlineno + 1]
3840+
self.assertEqual(candidates, [C])
3841+
38073842

38083843
class TestDescriptors(unittest.TestCase):
38093844
def test_set_name(self):
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
When :mod:`dataclasses` replaces a class with a slotted dataclass, the
2+
original class is now garbage collected again. Earlier changes in Python
3+
3.14 caused this class to remain in existence together with the replacement
4+
class synthesized by :mod:`dataclasses`.

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