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

Commitd764eb6

Browse files
JelleZijlstrancoghlanencukouserhiy-storchaka
authored andcommitted
gh-135228: When@DataClass(slots=True) replaces a dataclass, make the original class collectible (take 2) (GH-137047)
Remove the `__dict__` and `__weakref__` descriptors from the original class when creating a dataclass from it.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 commit6859b95)Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>Co-authored-by: Alyssa Coghlan <ncoghlan@gmail.com>Co-authored-by: Petr Viktorin <encukou@gmail.com>Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent0ccf244 commitd764eb6

File tree

5 files changed

+117
-7
lines changed

5 files changed

+117
-7
lines changed

‎Lib/dataclasses.py‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,10 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
12831283
if'__slots__'incls.__dict__:
12841284
raiseTypeError(f'{cls.__name__} already specifies __slots__')
12851285

1286+
# gh-102069: Remove existing __weakref__ descriptor.
1287+
# gh-135228: Make sure the original class can be garbage collected.
1288+
sys._clear_type_descriptors(cls)
1289+
12861290
# Create a new dict for our new class.
12871291
cls_dict=dict(cls.__dict__)
12881292
field_names=tuple(f.nameforfinfields(cls))
@@ -1300,12 +1304,6 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields):
13001304
# available in _MARKER.
13011305
cls_dict.pop(field_name,None)
13021306

1303-
# Remove __dict__ itself.
1304-
cls_dict.pop('__dict__',None)
1305-
1306-
# Clear existing `__weakref__` descriptor, it belongs to a previous type:
1307-
cls_dict.pop('__weakref__',None)# gh-102069
1308-
13091307
# And finally create the class.
13101308
qualname=getattr(cls,'__qualname__',None)
13111309
newcls=type(cls)(cls.__name__,cls.__bases__,cls_dict)

‎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 can now be garbage collected again. Earlier changes in Python
3+
3.14 caused this class to always remain in existence together with the replacement
4+
class synthesized by:mod:`dataclasses`.

‎Python/clinic/sysmodule.c.h‎

Lines changed: 32 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Python/sysmodule.c‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,6 +2640,47 @@ sys__baserepl_impl(PyObject *module)
26402640
Py_RETURN_NONE;
26412641
}
26422642

2643+
/*[clinic input]
2644+
sys._clear_type_descriptors
2645+
2646+
type: object(subclass_of='&PyType_Type')
2647+
/
2648+
2649+
Private function for clearing certain descriptors from a type's dictionary.
2650+
2651+
See gh-135228 for context.
2652+
[clinic start generated code]*/
2653+
2654+
staticPyObject*
2655+
sys__clear_type_descriptors_impl(PyObject*module,PyObject*type)
2656+
/*[clinic end generated code: output=5ad17851b762b6d9 input=dc536c97fde07251]*/
2657+
{
2658+
PyTypeObject*typeobj= (PyTypeObject*)type;
2659+
if (_PyType_HasFeature(typeobj,Py_TPFLAGS_IMMUTABLETYPE)) {
2660+
PyErr_SetString(PyExc_TypeError,"argument is immutable");
2661+
returnNULL;
2662+
}
2663+
PyObject*dict=_PyType_GetDict(typeobj);
2664+
PyObject*dunder_dict=NULL;
2665+
if (PyDict_Pop(dict,&_Py_ID(__dict__),&dunder_dict)<0) {
2666+
returnNULL;
2667+
}
2668+
PyObject*dunder_weakref=NULL;
2669+
if (PyDict_Pop(dict,&_Py_ID(__weakref__),&dunder_weakref)<0) {
2670+
PyType_Modified(typeobj);
2671+
Py_XDECREF(dunder_dict);
2672+
returnNULL;
2673+
}
2674+
PyType_Modified(typeobj);
2675+
// We try to hold onto a reference to these until after we call
2676+
// PyType_Modified(), in case their deallocation triggers somer user code
2677+
// that tries to do something to the type.
2678+
Py_XDECREF(dunder_dict);
2679+
Py_XDECREF(dunder_weakref);
2680+
Py_RETURN_NONE;
2681+
}
2682+
2683+
26432684
/*[clinic input]
26442685
sys._is_gil_enabled -> bool
26452686
@@ -2836,6 +2877,7 @@ static PyMethodDef sys_methods[] = {
28362877
SYS__STATS_DUMP_METHODDEF
28372878
#endif
28382879
SYS__GET_CPU_COUNT_CONFIG_METHODDEF
2880+
SYS__CLEAR_TYPE_DESCRIPTORS_METHODDEF
28392881
SYS__IS_GIL_ENABLED_METHODDEF
28402882
SYS__DUMP_TRACELETS_METHODDEF
28412883
{NULL,NULL}// sentinel

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp