diff --git a/Lib/test/test_slice.py b/Lib/test/test_slice.py index 8c4e670c9d4..5e76655ee6d 100644 --- a/Lib/test/test_slice.py +++ b/Lib/test/test_slice.py @@ -1,11 +1,13 @@ # tests for slice objects; in particular the indices method. -import unittest -from pickle import loads, dumps - import itertools import operator import sys +import unittest +import weakref + +from pickle import loads, dumps +from test import support def evaluate_slice_index(arg): @@ -240,5 +242,14 @@ class SliceTest(unittest.TestCase): self.assertEqual(s.indices(15), t.indices(15)) self.assertNotEqual(id(s), id(t)) + def test_cycle(self): + class myobj(): pass + o = myobj() + o.s = slice(o) + w = weakref.ref(o) + o = None + test_support.gc_collect() + self.assertIsNone(w()) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS b/Misc/NEWS index aca52337658..af2d65413a3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ Release date: tba Core and Builtins ----------------- +- Issue #26659: Make the builtin slice type support cycle collection. + - Issue #26718: super.__init__ no longer leaks memory if called multiple times. NOTE: A direct call of super.__init__ is not endorsed! diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 104952333a5..bf5eb3aeee8 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -119,7 +119,7 @@ PySlice_New(PyObject *start, PyObject *stop, PyObject *step) slice_cache = NULL; _Py_NewReference((PyObject *)obj); } else { - obj = PyObject_New(PySliceObject, &PySlice_Type); + obj = PyObject_GC_New(PySliceObject, &PySlice_Type); if (obj == NULL) return NULL; } @@ -135,6 +135,7 @@ PySlice_New(PyObject *start, PyObject *stop, PyObject *step) obj->start = start; obj->stop = stop; + _PyObject_GC_TRACK(obj); return (PyObject *) obj; } @@ -288,13 +289,14 @@ Create a slice object. This is used for extended slicing (e.g. a[0:10:2])."); static void slice_dealloc(PySliceObject *r) { + _PyObject_GC_UNTRACK(r); Py_DECREF(r->step); Py_DECREF(r->start); Py_DECREF(r->stop); if (slice_cache == NULL) slice_cache = r; else - PyObject_Del(r); + PyObject_GC_Del(r); } static PyObject * @@ -586,6 +588,15 @@ slice_richcompare(PyObject *v, PyObject *w, int op) return res; } +static int +slice_traverse(PySliceObject *v, visitproc visit, void *arg) +{ + Py_VISIT(v->start); + Py_VISIT(v->stop); + Py_VISIT(v->step); + return 0; +} + PyTypeObject PySlice_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "slice", /* Name of this type */ @@ -606,9 +617,9 @@ PyTypeObject PySlice_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ slice_doc, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)slice_traverse, /* tp_traverse */ 0, /* tp_clear */ slice_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */