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