Rev 69: Implement a custom get_referents function. in http://bazaar.launchpad.net/~jameinel/meliae/trunk
John Arbash Meinel
john at arbash-meinel.com
Thu Sep 10 20:51:12 BST 2009
At http://bazaar.launchpad.net/~jameinel/meliae/trunk
------------------------------------------------------------
revno: 69
revision-id: john at arbash-meinel.com-20090910195057-j7eclwh1koue8j4b
parent: john at arbash-meinel.com-20090909211215-odsdfqw8yd1e56fa
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: trunk
timestamp: Thu 2009-09-10 14:50:57 -0500
message:
Implement a custom get_referents function.
This mimics gc.get_referents() except it doesn't pay attention to the IS_GC flag.
The main reason for this is because things like Key objects don't participate
in garbage collection, but they *do* reference other objects.
-------------- next part --------------
=== modified file 'meliae/_scanner.pyx'
--- a/meliae/_scanner.pyx 2009-09-08 17:06:14 +0000
+++ b/meliae/_scanner.pyx 2009-09-10 19:50:57 +0000
@@ -32,6 +32,7 @@
cdef extern from "_scanner_core.h":
Py_ssize_t _size_of(object c_obj)
void _dump_object_info(FILE *, object c_obj, object nodump, int recurse)
+ object _get_referents(object c_obj)
_word_size = sizeof(Py_ssize_t)
@@ -59,3 +60,13 @@
if out == NULL:
raise TypeError('not a file')
_dump_object_info(out, obj, nodump, recurse_depth)
+
+
+def get_referents(object obj):
+ """Similar to gc.get_referents()
+
+ The main different is that gc.get_referents() only includes items that are
+ in the garbage collector. However, we want anything referred to by
+ tp_traverse.
+ """
+ return _get_referents(obj)
=== modified file 'meliae/_scanner_core.c'
--- a/meliae/_scanner_core.c 2009-09-09 21:12:15 +0000
+++ b/meliae/_scanner_core.c 2009-09-10 19:50:57 +0000
@@ -155,7 +155,7 @@
/* Objects without traverse are simple things without refs, and built-in
* types have a traverse, but they won't be part of gc.get_objects().
*/
- if (c_obj->ob_type->tp_traverse == NULL
+ if (Py_TYPE(c_obj)->tp_traverse == NULL
|| (PyType_Check(c_obj)
&& !PyType_HasFeature((PyTypeObject*)c_obj, Py_TPFLAGS_HEAPTYPE)))
{
@@ -301,19 +301,49 @@
fprintf(out, ", \"len\": %d", PyDict_Size(c_obj));
}
fprintf(out, ", \"refs\": [");
- if (c_obj->ob_type->tp_traverse != NULL) {
+ if (Py_TYPE(c_obj)->tp_traverse != NULL) {
info.first = 1;
- c_obj->ob_type->tp_traverse(c_obj, _dump_reference, &info);
+ Py_TYPE(c_obj)->tp_traverse(c_obj, _dump_reference, &info);
}
fprintf(out, "]}\n");
- if (c_obj->ob_type->tp_traverse != NULL && recurse != 0) {
+ if (Py_TYPE(c_obj)->tp_traverse != NULL && recurse != 0) {
if (recurse == 2) { /* Always dump one layer deeper */
- c_obj->ob_type->tp_traverse(c_obj, _dump_child, &info);
+ Py_TYPE(c_obj)->tp_traverse(c_obj, _dump_child, &info);
} else if (recurse == 1) {
/* strings and such aren't in gc.get_objects, so we need to dump
* them when they are referenced.
*/
- c_obj->ob_type->tp_traverse(c_obj, _dump_if_no_traverse, &info);
+ Py_TYPE(c_obj)->tp_traverse(c_obj, _dump_if_no_traverse, &info);
}
}
}
+
+static int
+_append_object(PyObject *visiting, void* data)
+{
+ PyObject *lst;
+ lst = (PyObject *)data;
+ if (lst == NULL) {
+ return -1;
+ }
+ if (PyList_Append(data, visiting) == -1) {
+ return -1;
+ }
+ return 0;
+}
+/**
+ * Return a PyList of all objects referenced via tp_traverse.
+ */
+PyObject *_get_referents(PyObject *c_obj)
+{
+ PyObject *lst;
+
+ lst = PyList_New(0);
+ if (lst == NULL) {
+ return NULL;
+ }
+ if (Py_TYPE(c_obj)->tp_traverse != NULL) {
+ Py_TYPE(c_obj)->tp_traverse(c_obj, _append_object, lst);
+ }
+ return lst;
+}
=== modified file 'meliae/_scanner_core.h'
--- a/meliae/_scanner_core.h 2009-04-03 04:52:43 +0000
+++ b/meliae/_scanner_core.h 2009-09-10 19:50:57 +0000
@@ -39,6 +39,11 @@
*/
void _dump_object_info(FILE *out, PyObject *c_obj, PyObject *nodump, int recurse);
+/**
+ * Return a PyList of all objects referenced via tp_traverse.
+ */
+PyObject *_get_referents(PyObject *c_obj);
+
#endif // _SCANNER_CORE_H_
=== modified file 'meliae/scanner.py'
--- a/meliae/scanner.py 2009-09-08 18:28:52 +0000
+++ b/meliae/scanner.py 2009-09-10 19:50:57 +0000
@@ -26,6 +26,7 @@
size_of = _scanner.size_of
+get_referents = _scanner.get_referents
def dump_all_referenced(outf, obj):
@@ -42,7 +43,7 @@
seen.add(id_next)
# We will recurse here, so tell dump_object_info to not recurse
_scanner.dump_object_info(outf, next, recurse_depth=0)
- for ref in gc.get_referents(next):
+ for ref in get_referents(next):
if id(ref) not in seen:
pending.append(ref)
@@ -104,7 +105,7 @@
continue
seen.add(id_item)
total_size += size_of(item)
- for child in gc.get_referents(item):
+ for child in get_referents(item):
if id(child) not in seen:
last_item += 1
if len(pending) > last_item:
@@ -128,7 +129,7 @@
continue
seen.add(id_item)
all.append(item)
- for child in gc.get_referents(item):
+ for child in get_referents(item):
if id(child) not in seen:
last_item += 1
if len(pending) > last_item:
=== modified file 'meliae/tests/test__scanner.py'
--- a/meliae/tests/test__scanner.py 2009-09-08 17:19:16 +0000
+++ b/meliae/tests/test__scanner.py 2009-09-10 19:50:57 +0000
@@ -424,3 +424,10 @@
class MyOldClass:
pass
self.assertDumpInfo(MyOldClass)
+
+
+class TestGetReferents(tests.TestCase):
+
+ def test_list_referents(self):
+ l = ['one', 2, object(), 4.0]
+ self.assertEqual(gc.get_referents(l), _scanner.get_referents(l))
More information about the bazaar-commits
mailing list