Finished implementing gc.get_referrents(): dealt with error and end

cases, wrote docs, added a test.
This commit is contained in:
Tim Peters 2003-04-08 16:39:48 +00:00
parent fb2ab4d5ae
commit 0f81ab6d88
4 changed files with 58 additions and 7 deletions

View File

@ -99,6 +99,19 @@ objects, call \function{collect()} before calling
\versionadded{2.2} \versionadded{2.2}
\end{funcdesc} \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 The following variable is provided for read-only access (you can
mutate its value but should not rebind it): mutate its value but should not rebind it):

View File

@ -4,7 +4,7 @@ import gc
def expect(actual, expected, name): def expect(actual, expected, name):
if actual != expected: if actual != expected:
raise TestFailed, "test_%s: actual %d, expected %d" % ( raise TestFailed, "test_%s: actual %r, expected %r" % (
name, actual, expected) name, actual, expected)
def expect_nonzero(actual, name): def expect_nonzero(actual, name):
@ -304,6 +304,29 @@ def test_boom2():
expect(gc.collect(), 4, "boom2") expect(gc.collect(), 4, "boom2")
expect(len(gc.garbage), garbagelen, "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(): def test_all():
gc.collect() # Delete 2nd generation garbage gc.collect() # Delete 2nd generation garbage
run_test("lists", test_list) run_test("lists", test_list)
@ -324,6 +347,7 @@ def test_all():
run_test("trashcan", test_trashcan) run_test("trashcan", test_trashcan)
run_test("boom", test_boom) run_test("boom", test_boom)
run_test("boom2", test_boom2) run_test("boom2", test_boom2)
run_test("get_referrents", test_get_referrents)
def test(): def test():
if verbose: if verbose:

View File

@ -49,6 +49,11 @@ Core and builtins
Extension modules 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 iconv module has been removed from this release.
- The platform-independent routines for packing floats in IEEE formats - The platform-independent routines for packing floats in IEEE formats

View File

@ -857,12 +857,11 @@ gc_get_referrers(PyObject *self, PyObject *args)
return result; return result;
} }
/* Append obj to list; return true if error (out of memory), false if OK. */
static int static int
referrentsvisit(PyObject *obj, PyObject *list) referrentsvisit(PyObject *obj, PyObject *list)
{ {
if (PyList_Append(list, obj) < 0) return PyList_Append(list, obj) < 0;
return 1;
return 0;
} }
PyDoc_STRVAR(gc_get_referrents__doc__, PyDoc_STRVAR(gc_get_referrents__doc__,
@ -874,13 +873,23 @@ gc_get_referrents(PyObject *self, PyObject *args)
{ {
int i; int i;
PyObject *result = PyList_New(0); PyObject *result = PyList_New(0);
if (result == NULL)
return NULL;
for (i = 0; i < PyTuple_GET_SIZE(args); i++) { for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
traverseproc traverse;
PyObject *obj = PyTuple_GET_ITEM(args, i); PyObject *obj = PyTuple_GET_ITEM(args, i);
traverseproc traverse = obj->ob_type->tp_traverse;
if (!traverse) if (! PyObject_IS_GC(obj))
continue; continue;
if (traverse(obj, (visitproc)referrentsvisit, result)) traverse = obj->ob_type->tp_traverse;
if (! traverse)
continue;
if (traverse(obj, (visitproc)referrentsvisit, result)) {
Py_DECREF(result);
return NULL; return NULL;
}
} }
return result; return result;
} }