Skip to content

bpo-36016: Allow gc.getobjects to return the objects in a specific generation #11909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 23, 2019
7 changes: 5 additions & 2 deletions Doc/library/gc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ The :mod:`gc` module provides the following functions:
Return the debugging flags currently set.


.. function:: get_objects()
.. function:: get_objects(generation=None)

Returns a list of all objects tracked by the collector, excluding the list
returned.
returned. If *generation* is not None, return only the objects tracked by
the collector that are in that generation.

.. versionchanged:: 3.8
New *generation* parameter.

.. function:: get_stats()

Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ gettext
Added :func:`~gettext.pgettext` and its variants.
(Contributed by Franz Glasner, Éric Araujo, and Cheryl Sabella in :issue:`2504`.)


gc
--

:func:`~gc.get_objects` can now receive an optional *generation* parameter
indicating a generation to get objects from. Contributed in
:issue:`36016` by Pablo Galindo.


gzip
----

Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,38 @@ def test_freeze(self):
gc.unfreeze()
self.assertEqual(gc.get_freeze_count(), 0)

def test_get_objects(self):
gc.collect()
l = []
l.append(l)
self.assertIn(l, gc.get_objects(generation=0))
self.assertNotIn(l, gc.get_objects(generation=1))
self.assertNotIn(l, gc.get_objects(generation=2))
gc.collect(generation=0)
self.assertNotIn(l, gc.get_objects(generation=0))
self.assertIn(l, gc.get_objects(generation=1))
self.assertNotIn(l, gc.get_objects(generation=2))
gc.collect(generation=1)
self.assertNotIn(l, gc.get_objects(generation=0))
self.assertNotIn(l, gc.get_objects(generation=1))
self.assertIn(l, gc.get_objects(generation=2))
gc.collect(generation=2)
self.assertNotIn(l, gc.get_objects(generation=0))
self.assertNotIn(l, gc.get_objects(generation=1))
self.assertIn(l, gc.get_objects(generation=2))
del l
gc.collect()

def test_get_objects_arguments(self):
gc.collect()
self.assertEqual(len(gc.get_objects()),
len(gc.get_objects(generation=None)))

self.assertRaises(ValueError, gc.get_objects, 1000)
self.assertRaises(ValueError, gc.get_objects, -1000)
self.assertRaises(TypeError, gc.get_objects, "1")
self.assertRaises(TypeError, gc.get_objects, 1.234)


class GCCallbackTests(unittest.TestCase):
def setUp(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
``gc.get_objects`` can now receive an optional parameter indicating a
generation to get objects from. Patch by Pablo Galindo.
32 changes: 25 additions & 7 deletions Modules/clinic/gcmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 39 additions & 5 deletions Modules/gcmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1502,27 +1502,61 @@ gc_get_referents(PyObject *self, PyObject *args)

/*[clinic input]
gc.get_objects
generation: Py_ssize_t(accept={int, NoneType}, c_default="-1") = None
Generation to extract the objects from.

Return a list of objects tracked by the collector (excluding the list returned).

If generation is not None, return only the objects tracked by the collector
that are in that generation.
[clinic start generated code]*/

static PyObject *
gc_get_objects_impl(PyObject *module)
/*[clinic end generated code: output=fcb95d2e23e1f750 input=9439fe8170bf35d8]*/
gc_get_objects_impl(PyObject *module, Py_ssize_t generation)
/*[clinic end generated code: output=48b35fea4ba6cb0e input=ef7da9df9806754c]*/
{
int i;
PyObject* result;

result = PyList_New(0);
if (result == NULL)
if (result == NULL) {
return NULL;
}

/* If generation is passed, we extract only that generation */
if (generation != -1) {
if (generation >= NUM_GENERATIONS) {
PyErr_Format(PyExc_ValueError,
"generation parameter must be less than the number of "
"available generations (%i)",
NUM_GENERATIONS);
goto error;
}

if (generation < 0) {
PyErr_SetString(PyExc_ValueError,
"generation parameter cannot be negative");
goto error;
}

if (append_objects(result, GEN_HEAD(generation))) {
goto error;
}

return result;
}

/* If generation is not passed or None, get all objects from all generations */
for (i = 0; i < NUM_GENERATIONS; i++) {
if (append_objects(result, GEN_HEAD(i))) {
Py_DECREF(result);
return NULL;
goto error;
}
}
return result;

error:
Py_DECREF(result);
return NULL;
}

/*[clinic input]
Expand Down