Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6e1b31b

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 thanGH-135230.This may be a breaking change if people intentionally keep the original class aroundwhen using `@dataclass(slots=True)`, and then use `__dict__` or `__weakref__` on theoriginal class.(cherry picked from commit46cbdf9)Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>Co-authored-by: Alyssa Coghlan <ncoghlan@gmail.com>
1 parentcaef946 commit6e1b31b

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__"incls.__dict__:
1346+
delcls.__weakref__
1347+
13411348
returnnewcls
13421349

13431350

@@ -1732,3 +1739,11 @@ def _replace(self, /, **changes):
17321739
# changes that aren't fields, this will correctly raise a
17331740
# TypeError.
17341741
returnself.__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+
returnother
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+
deftest_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+
defmake_simple():
3811+
@dataclass(slots=True)
3812+
classSlotsTest:
3813+
pass
3814+
3815+
returnSlotsTest
3816+
3817+
defmake_with_annotations():
3818+
@dataclass(slots=True)
3819+
classSlotsTest:
3820+
x:int
3821+
3822+
returnSlotsTest
3823+
3824+
defmake_with_annotations_and_method():
3825+
@dataclass(slots=True)
3826+
classSlotsTest:
3827+
x:int
3828+
3829+
defmethod(self)->int:
3830+
returnself.x
3831+
3832+
returnSlotsTest
3833+
3834+
formakein (make_simple,make_with_annotations,make_with_annotations_and_method):
3835+
withself.subTest(make=make):
3836+
C=make()
3837+
support.gc_collect()
3838+
candidates= [clsforclsinobject.__subclasses__()ifcls.__name__=='SlotsTest'
3839+
andcls.__firstlineno__==make.__code__.co_firstlineno+1]
3840+
self.assertEqual(candidates, [C])
3841+
38073842

38083843
classTestDescriptors(unittest.TestCase):
38093844
deftest_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)

[8]ページ先頭

©2009-2025 Movatter.jp