Given an object, this tool throws up a gtk tree widget that maps all the references found. It dynamically builds the tree, which means it can handle large amounts of data and circular references.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class Browser:
def make_row( self, piter, name, value ):
info = repr(value)
if not hasattr(value, "__dict__"):
if len(info) > 80:
# it's a big list, or dict etc.
info = info[:80] + "..."
_piter = self.treestore.append( piter, [ name, type(value).__name__, info ] )
return _piter
def make_instance( self, value, piter ):
if hasattr( value, "__dict__" ):
for _name, _value in value.__dict__.items():
_piter = self.make_row( piter, "."+_name, _value )
_path = self.treestore.get_path( _piter )
self.otank[ _path ] = (_name, _value)
def make_mapping( self, value, piter ):
keys = []
if hasattr( value, "keys" ):
keys = value.keys()
elif hasattr( value, "__len__"):
keys = range( len(value) )
for key in keys:
_name = "[%s]"%str(key)
_piter = self.make_row( piter, _name, value[key] )
_path = self.treestore.get_path( _piter )
self.otank[ _path ] = (_name, value[key])
def make(self, name=None, value=None, path=None, depth=1):
if path is None:
# make root node
piter = self.make_row( None, name, value )
path = self.treestore.get_path( piter )
self.otank[ path ] = (name, value)
else:
name, value = self.otank[ path ]
piter = self.treestore.get_iter( path )
if not self.treestore.iter_has_child( piter ):
self.make_mapping( value, piter )
self.make_instance( value, piter )
if depth:
for i in range( self.treestore.iter_n_children( piter ) ):
self.make( path = path+(i,), depth = depth - 1 )
def row_expanded( self, treeview, piter, path ):
self.make( path = path )
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return gtk.FALSE
def __init__(self, name, value):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Browser")
self.window.set_size_request(512, 320)
self.window.connect("delete_event", self.delete_event)
# we will store the name, the type name, and the repr
columns = [str,str,str]
self.treestore = gtk.TreeStore(*columns)
# the otank tells us what object we put at each node in the tree
self.otank = {} # map path -> (name,value)
self.make( name, value )
self.treeview = gtk.TreeView(self.treestore)
self.treeview.connect("row-expanded", self.row_expanded )
self.tvcolumns = [ gtk.TreeViewColumn() for _type in columns ]
i = 0
for tvcolumn in self.tvcolumns:
self.treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', i)
i = i + 1
self.window.add(self.treeview)
self.window.show_all()
def dump( name, value ):
browser = Browser( name, value )
gtk.main()
def test():
class Nil:
pass
a = Nil()
b = Nil()
c = Nil()
d = Nil()
a.b=b
b.c=c
c.d=d
d.a=a # circular chain
dump( "a", a )
if __name__ == "__main__":
test()
|
This script is useful for debugging the suspect object that has just caused an exception by calling the dump( ) routine from the except clause of your script. It is also good for checking through old pickled data, or other complex object stores.
The make* routines can be adjusted to taste: for example to search through dir(value) to obtain information about modules and classes.
This is inspired by a browse.py script supplied with the pygtk distribution, that alas, has not been updated to pygtk-2.x. See also the TreeView example in the excellent pygtk tutorial by John Finlay.
Download
Copy to clipboard
This code is useful but is broken when an object contains Python 'sets', eg
x = set([3,4,5])
Sets are different because they have a __len__ attribute, but are not indexable.
It seems likely that this highlights a more general failing of the code. If I manage to work out a better implementation, I'll post it here.
See here: http://ascendcode.cheme.cmu.edu/viewvc.cgi/code/branches/extfn/pygtk/canvas/obrowser.py