import gc, sys, weakref
class ObjTracker:
"""
A debugging aid that shows you how an object is travelling through
code.
"""
def install(self):
"""
Start tracking objects.
WARNING: NO objects will be collected between a call to
L{install} and L{reset}.
"""
self.reset()
sys.settrace(self.globalTrace)
def reset(self):
"""
Clean everything out. This gives a chance to Python to clean
up all of its objects.
"""
self.refs = []
self.objsToFuncs = {}
def remove(self):
"""
Remove the tracer. Note that this will remove whatever trace
function you have installed, whether it is the objtracker or
not.
"""
sys.settrace(None)
def getObjectFlow(self, o):
"""
Get the flow the given object took through code.
"""
return self.objsToFuncs[id(o)]
def globalTrace(self, frame, event, arg):
return self.localTrace
def localTrace(self, frame, event, arg):
referents = self.getReferents(frame)
for o in referents:
self.refs.append(o) # keep a reference around
l = self.objsToFuncs.setdefault(id(o), [])
fname = frame.f_code.co_name
l.append((event, fname, frame.f_lineno))
return self.localTrace
def getReferents(self, obj, cache=None):
"""
Return a list of all objects that an object references, no
matter how far away it is; this is cycle-safe.
"""
if cache is None:
cache = {}
if (id(obj) in cache):
return
for referent in gc.get_referents(obj):
cache[id(referent)] = referent
self.getReferents(referent, cache)
return cache.values()
if __name__ == '__main__':
class Foo:
pass
o = Foo()
def foo(x):
bar(x)
return baz(x)
def bar(y):
hash(y)
return y == 2
def baz(z):
while 1:
if z is z:
break
return z
ot = ObjTracker()
ot.install()
foo(o)
ot.remove()
import pprint
pprint.pprint(ot.getObjectFlow(o))
## prints:
##[('line', 'foo', 75),
## ('line', 'bar', 79),
## ('line', 'bar', 80),
## ('return', 'bar', 80),
## ('line', 'foo', 76),
## ('line', 'baz', 83),
## ('line', 'baz', 84),
## ('line', 'baz', 85),
## ('line', 'baz', 86),
## ('return', 'baz', 86),
## ('return', 'foo', 76)]
syntax highlighted by Code2HTML, v. 0.9.1