[Python-checkins] bpo-36016: Add generation option to gc.getobjects() (GH-11909)

Inada Naoki webhook-mailer at python.org
Fri Feb 22 22:02:11 EST 2019


https://github.com/python/cpython/commit/175421b58cc97a2555e474f479f30a6c5d2250b0
commit: 175421b58cc97a2555e474f479f30a6c5d2250b0
branch: master
author: Pablo Galindo <Pablogsal at gmail.com>
committer: Inada Naoki <methane at users.noreply.github.com>
date: 2019-02-23T12:02:06+09:00
summary:

bpo-36016: Add generation option to gc.getobjects() (GH-11909)

files:
A Misc/NEWS.d/next/Core and Builtins/2019-02-17-20-23-54.bpo-36016.5Hns-f.rst
M Doc/library/gc.rst
M Doc/whatsnew/3.8.rst
M Lib/test/test_gc.py
M Modules/clinic/gcmodule.c.h
M Modules/gcmodule.c

diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst
index 722a0e804314..084cd6ac257e 100644
--- a/Doc/library/gc.rst
+++ b/Doc/library/gc.rst
@@ -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()
 
diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst
index bf7300db0945..a531d2d56af6 100644
--- a/Doc/whatsnew/3.8.rst
+++ b/Doc/whatsnew/3.8.rst
@@ -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
 ----
 
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 16b22422528f..65e74d4759ce 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -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):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-02-17-20-23-54.bpo-36016.5Hns-f.rst b/Misc/NEWS.d/next/Core and Builtins/2019-02-17-20-23-54.bpo-36016.5Hns-f.rst
new file mode 100644
index 000000000000..078be94a9d81
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-02-17-20-23-54.bpo-36016.5Hns-f.rst	
@@ -0,0 +1,2 @@
+``gc.get_objects`` can now receive an optional parameter indicating a
+generation to get objects from. Patch by Pablo Galindo.
diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h
index 4fd2ea0720ac..eece04597b10 100644
--- a/Modules/clinic/gcmodule.c.h
+++ b/Modules/clinic/gcmodule.c.h
@@ -216,21 +216,39 @@ gc_get_count(PyObject *module, PyObject *Py_UNUSED(ignored))
 }
 
 PyDoc_STRVAR(gc_get_objects__doc__,
-"get_objects($module, /)\n"
+"get_objects($module, /, generation=None)\n"
 "--\n"
 "\n"
-"Return a list of objects tracked by the collector (excluding the list returned).");
+"Return a list of objects tracked by the collector (excluding the list returned).\n"
+"\n"
+"  generation\n"
+"    Generation to extract the objects from.\n"
+"\n"
+"If generation is not None, return only the objects tracked by the collector\n"
+"that are in that generation.");
 
 #define GC_GET_OBJECTS_METHODDEF    \
-    {"get_objects", (PyCFunction)gc_get_objects, METH_NOARGS, gc_get_objects__doc__},
+    {"get_objects", (PyCFunction)(void(*)(void))gc_get_objects, METH_FASTCALL|METH_KEYWORDS, gc_get_objects__doc__},
 
 static PyObject *
-gc_get_objects_impl(PyObject *module);
+gc_get_objects_impl(PyObject *module, Py_ssize_t generation);
 
 static PyObject *
-gc_get_objects(PyObject *module, PyObject *Py_UNUSED(ignored))
+gc_get_objects(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
-    return gc_get_objects_impl(module);
+    PyObject *return_value = NULL;
+    static const char * const _keywords[] = {"generation", NULL};
+    static _PyArg_Parser _parser = {"|O&:get_objects", _keywords, 0};
+    Py_ssize_t generation = -1;
+
+    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
+        _Py_convert_optional_to_ssize_t, &generation)) {
+        goto exit;
+    }
+    return_value = gc_get_objects_impl(module, generation);
+
+exit:
+    return return_value;
 }
 
 PyDoc_STRVAR(gc_get_stats__doc__,
@@ -331,4 +349,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored))
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=5aa5fdc259503d5f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d692bf475f0bb096 input=a9049054013a1b77]*/
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 64140c1b8899..fad1356d6b44 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -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]



More information about the Python-checkins mailing list