mirror of https://github.com/python/cpython
bpo-44553 : Implement GC methods for types.Union (GH-26993)
This commit is contained in:
parent
01331f1a3c
commit
1097384ce9
|
@ -1,8 +1,9 @@
|
|||
# Python test set -- part 6, built-in types
|
||||
|
||||
from test.support import run_with_locale
|
||||
from test.support import run_with_locale, cpython_only
|
||||
import collections.abc
|
||||
from collections import namedtuple
|
||||
import gc
|
||||
import inspect
|
||||
import pickle
|
||||
import locale
|
||||
|
@ -756,6 +757,23 @@ class TypesTests(unittest.TestCase):
|
|||
with self.assertRaises(ZeroDivisionError):
|
||||
str | TypeVar()
|
||||
|
||||
@cpython_only
|
||||
def test_or_type_operator_reference_cycle(self):
|
||||
if not hasattr(sys, 'gettotalrefcount'):
|
||||
self.skipTest('Cannot get total reference count.')
|
||||
gc.collect()
|
||||
before = sys.gettotalrefcount()
|
||||
for _ in range(30):
|
||||
T = typing.TypeVar('T')
|
||||
U = int | list[T]
|
||||
T.blah = U
|
||||
del T
|
||||
del U
|
||||
gc.collect()
|
||||
leeway = 15
|
||||
self.assertLessEqual(sys.gettotalrefcount() - before, leeway,
|
||||
msg='Check for union reference leak.')
|
||||
|
||||
def test_ellipsis_type(self):
|
||||
self.assertIsInstance(Ellipsis, types.EllipsisType)
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Implement GC methods for ``types.Union`` to break reference cycles and
|
||||
prevent memory leaks.
|
|
@ -1,5 +1,6 @@
|
|||
// types.Union -- used to represent e.g. Union[int, str], int | str
|
||||
#include "Python.h"
|
||||
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
|
||||
#include "pycore_unionobject.h"
|
||||
#include "structmember.h"
|
||||
|
||||
|
@ -14,10 +15,20 @@ unionobject_dealloc(PyObject *self)
|
|||
{
|
||||
unionobject *alias = (unionobject *)self;
|
||||
|
||||
_PyObject_GC_UNTRACK(self);
|
||||
|
||||
Py_XDECREF(alias->args);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
static int
|
||||
union_traverse(PyObject *self, visitproc visit, void *arg)
|
||||
{
|
||||
unionobject *alias = (unionobject *)self;
|
||||
Py_VISIT(alias->args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Py_hash_t
|
||||
union_hash(PyObject *self)
|
||||
{
|
||||
|
@ -437,8 +448,9 @@ PyTypeObject _Py_UnionType = {
|
|||
.tp_basicsize = sizeof(unionobject),
|
||||
.tp_dealloc = unionobject_dealloc,
|
||||
.tp_alloc = PyType_GenericAlloc,
|
||||
.tp_free = PyObject_Del,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_free = PyObject_GC_Del,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
.tp_traverse = union_traverse,
|
||||
.tp_hash = union_hash,
|
||||
.tp_getattro = PyObject_GenericGetAttr,
|
||||
.tp_members = union_members,
|
||||
|
@ -472,15 +484,16 @@ _Py_Union(PyObject *args)
|
|||
}
|
||||
}
|
||||
|
||||
result = PyObject_New(unionobject, &_Py_UnionType);
|
||||
result = PyObject_GC_New(unionobject, &_Py_UnionType);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result->args = dedup_and_flatten_args(args);
|
||||
if (result->args == NULL) {
|
||||
Py_DECREF(result);
|
||||
PyObject_GC_Del(result);
|
||||
return NULL;
|
||||
}
|
||||
_PyObject_GC_TRACK(result);
|
||||
return (PyObject*)result;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue