]> granicus.if.org Git - python/commitdiff
Finished implementing gc.get_referrents(): dealt with error and end
authorTim Peters <tim.peters@gmail.com>
Tue, 8 Apr 2003 16:39:48 +0000 (16:39 +0000)
committerTim Peters <tim.peters@gmail.com>
Tue, 8 Apr 2003 16:39:48 +0000 (16:39 +0000)
cases, wrote docs, added a test.

Doc/lib/libgc.tex
Lib/test/test_gc.py
Misc/NEWS
Modules/gcmodule.c

index 310c5822b8c7b3f927c0bd4a96be742657268770..eea8d8c0f4794b6a1ef9db498b4eb0461696520f 100644 (file)
@@ -99,6 +99,19 @@ objects, call \function{collect()} before calling
 \versionadded{2.2}
 \end{funcdesc}
 
+\begin{funcdesc}{get_referrents}{*objs}
+Return a list of objects directly referred to by any of the arguments.
+The referrents returned are those objects visited by the arguments'
+C-level \cfunction{tp_traverse} methods (if any), and may not be all
+objects actually directly reachable.  \cfunction{tp_traverse} methods
+are supported only by objects that support garbage collection, and are
+only required to visit objects that may be involved in a cycle.  So,
+for example, if an integer is directly reachable from an argument, that
+integer object may or may not appear in the result list.
+
+\versionadded{2.3}
+\end{funcdesc}
+
 The following variable is provided for read-only access (you can
 mutate its value but should not rebind it):
 
index 1fbd5081cce4e483c4e344a8c09cb0cfa5df7c09..f0d5e19b4308784d41347aa832ff54fc71a88a0e 100644 (file)
@@ -4,7 +4,7 @@ import gc
 
 def expect(actual, expected, name):
     if actual != expected:
-        raise TestFailed, "test_%s: actual %d, expected %d" % (
+        raise TestFailed, "test_%s: actual %r, expected %r" % (
             name, actual, expected)
 
 def expect_nonzero(actual, name):
@@ -304,6 +304,29 @@ def test_boom2():
     expect(gc.collect(), 4, "boom2")
     expect(len(gc.garbage), garbagelen, "boom2")
 
+def test_get_referrents():
+    alist = [1, 3, 5]
+    got = gc.get_referrents(alist)
+    got.sort()
+    expect(got, alist, "get_referrents")
+
+    atuple = tuple(alist)
+    got = gc.get_referrents(atuple)
+    got.sort()
+    expect(got, alist, "get_referrents")
+
+    adict = {1: 3, 5: 7}
+    expected = [1, 3, 5, 7]
+    got = gc.get_referrents(adict)
+    got.sort()
+    expect(got, expected, "get_referrents")
+
+    got = gc.get_referrents([1, 2], {3: 4}, (0, 0, 0))
+    got.sort()
+    expect(got, [0, 0] + range(5), "get_referrents")
+
+    expect(gc.get_referrents(1, 'a', 4j), [], "get_referrents")
+
 def test_all():
     gc.collect() # Delete 2nd generation garbage
     run_test("lists", test_list)
@@ -324,6 +347,7 @@ def test_all():
     run_test("trashcan", test_trashcan)
     run_test("boom", test_boom)
     run_test("boom2", test_boom2)
+    run_test("get_referrents", test_get_referrents)
 
 def test():
     if verbose:
index 667c89eac199093032557dc8b99af79e00db4047..7b8f8c0347a57892b1b7df3bff371dfdfaa64a08 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,11 @@ Core and builtins
 Extension modules
 -----------------
 
+- New function gc.get_referrents(obj) returns a list of objects
+  directly referenced by obj.  In effect, it exposes what the object's
+  tp_traverse slot does, and can be helpful when debugging memory
+  leaks.
+
 - The iconv module has been removed from this release.
 
 - The platform-independent routines for packing floats in IEEE formats
index 9017363bbdfdbbdf6008c1b1c1d48390baf738f2..b0faad26c0d95e4f89f18c7538570dd7ef601b88 100644 (file)
@@ -857,12 +857,11 @@ gc_get_referrers(PyObject *self, PyObject *args)
        return result;
 }
 
+/* Append obj to list; return true if error (out of memory), false if OK. */
 static int
 referrentsvisit(PyObject *obj, PyObject *list)
 {
-       if (PyList_Append(list, obj) < 0)
-               return 1;
-       return 0;
+       return PyList_Append(list, obj) < 0;
 }
 
 PyDoc_STRVAR(gc_get_referrents__doc__,
@@ -874,13 +873,23 @@ gc_get_referrents(PyObject *self, PyObject *args)
 {
        int i;
        PyObject *result = PyList_New(0);
+
+       if (result == NULL)
+               return NULL;
+
        for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
+               traverseproc traverse;
                PyObject *obj = PyTuple_GET_ITEM(args, i);
-               traverseproc traverse = obj->ob_type->tp_traverse;
-               if (!traverse)
+
+               if (! PyObject_IS_GC(obj))
+                       continue;
+               traverse = obj->ob_type->tp_traverse;
+               if (! traverse)
                        continue;
-               if (traverse(obj, (visitproc)referrentsvisit, result))
+               if (traverse(obj, (visitproc)referrentsvisit, result)) {
+                       Py_DECREF(result);
                        return NULL;
+               }
        }
        return result;
 }