breadth-first search

This commit is contained in:
Darren Ranalli 2006-10-13 00:10:57 +00:00
parent f8c2e9d790
commit 431d855513

View File

@ -1,28 +1,49 @@
from direct.showbase.PythonUtil import Stack, fastRepr, invertDictLossless
from direct.showbase.PythonUtil import Queue, fastRepr, invertDictLossless
from direct.showbase.PythonUtil import itype, safeRepr
import types
"""
import types;from direct.showbase.ContainerReport import ContainerReport;ContainerReport().log()
"""
class ContainerReport:
def __init__(self, rootObj, rootObjName=None):
self._rootObj = rootObj
if rootObjName is None:
rootObjName = repr(rootObj)
PrivateIds = set()
def __init__(self, name, log=False, limit=None):
self._name = name
self._visitedIds = set()
self._id2pathStr = {}
self._id2container = {}
self._id2len = {}
self._stack = Stack()
self._stack.push(rootObj)
self._id2pathStr[id(rootObj)] = rootObjName
self._type2id2len = {}
self._instanceDictIds = set()
# for breadth-first searching
self._queue = Queue()
ContainerReport.PrivateIds.update(set([
id(self._visitedIds),
id(self._id2pathStr),
id(self._id2container),
id(self._type2id2len),
id(self._queue),
id(self._instanceDictIds),
]))
self._queue.push(__builtins__)
self._id2pathStr[id(__builtins__)] = ''
self._traverse()
if log:
self.log(limit=limit)
def _examine(self, obj):
# return False if it's an object that can't contain or lead to other objects
if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
types.BuiltinMethodType, types.ComplexType,
types.FloatType, types.IntType, types.LongType,
types.NoneType, types.NotImplementedType,
types.TypeType, types.CodeType):
types.TypeType, types.CodeType, types.FunctionType):
return False
# if it's an internal object, ignore it
if id(obj) in ContainerReport.PrivateIds:
return False
# this object might lead to more objects. put it on the queue
self._queue.push(obj)
# if it's a container, put it in the tables
try:
length = len(obj)
except:
@ -30,39 +51,56 @@ class ContainerReport:
if length is not None and length > 0:
objId = id(obj)
self._id2container[objId] = obj
self._id2len[objId] = length
self._type2id2len.setdefault(type(obj), {})
self._type2id2len[type(obj)][objId] = length
return True
def _traverse(self):
while len(self._stack) > 0:
obj = self._stack.pop()
#print '_traverse: %s' % fastRepr(obj, 30)
while len(self._queue) > 0:
parentObj = self._queue.pop()
#print '%s: %s, %s' % (id(parentObj), type(parentObj), fastRepr(parentObj))
isInstanceDict = False
if id(parentObj) in self._instanceDictIds:
isInstanceDict = True
try:
dirList = dir(obj)
if parentObj.__class__.__name__ == 'method-wrapper':
continue
except:
pass
else:
for name in dirList:
if name[-2:] == '__':
if type(parentObj) in (types.StringType, types.UnicodeType):
continue
attr = getattr(obj, name)
if type(parentObj) in (types.ModuleType, types.InstanceType):
child = parentObj.__dict__
if self._examine(child):
assert _equal(self._queue.back(), child)
self._instanceDictIds.add(id(child))
self._id2pathStr[id(child)] = str(self._id2pathStr[id(parentObj)])
continue
if type(parentObj) is types.DictType:
key = None
attr = None
for key, attr in parentObj.items():
if id(attr) not in self._visitedIds:
self._visitedIds.add(id(attr))
if self._examine(attr):
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '.%s' % name
self._stack.push(attr)
if type(obj) not in (types.StringType, types.UnicodeType):
if type(obj) is types.DictType:
keys = obj.keys()
for key in keys:
attr = obj[key]
if id(attr) not in self._visitedIds:
self._visitedIds.add(id(attr))
if self._examine(attr):
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '[%s]' % safeRepr(key)
self._stack.push(attr)
elif type(obj) is not types.FileType:
assert _equal(self._queue.back(), attr)
if parentObj is __builtins__:
self._id2pathStr[id(attr)] = key
else:
if isInstanceDict:
self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '.%s' % key
else:
self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % safeRepr(key)
del key
del attr
continue
if type(parentObj) is not types.FileType:
try:
itr = iter(obj)
itr = iter(parentObj)
except:
pass
else:
@ -73,22 +111,64 @@ class ContainerReport:
attr = itr.next()
except:
# some custom classes don't do well when iterated
attr = None
break
if id(attr) not in self._visitedIds:
self._visitedIds.add(id(attr))
if self._examine(attr):
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '[%s]' % index
self._stack.push(attr)
assert _equal(self._queue.back(), attr)
self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % index
index += 1
del attr
except StopIteration, e:
pass
del itr
continue
def __repr__(self):
len2ids = invertDictLossless(self._id2len)
try:
childNames = dir(parentObj)
except:
pass
else:
childName = None
child = None
for childName in childNames:
child = getattr(parentObj, childName)
if id(child) not in self._visitedIds:
self._visitedIds.add(id(child))
if self._examine(child):
assert _equal(self._queue.back(), child)
self._id2pathStr[id(child)] = self._id2pathStr[id(parentObj)] + '.%s' % childName
del childName
del child
continue
def _outputType(self, type, limit=None):
if type not in self._type2id2len:
return
len2ids = invertDictLossless(self._type2id2len[type])
lengths = len2ids.keys()
lengths.sort()
lengths.reverse()
print '====='
print '===== %s' % type
count = 0
stop = False
for l in lengths:
for id in len2ids[l]:
obj = self._id2container[id]
print '%s: %s: %s' % (l, repr(itype(obj)), self._id2pathStr[id])
print '%s: %s' % (l, self._id2pathStr[id])
count += 1
if limit is not None and count >= limit:
return
def _output(self, **kArgs):
initialTypes = (types.DictType, types.ListType, types.TupleType)
for type in initialTypes:
self._outputType(type, **kArgs)
otherTypes = set(self._type2id2len.keys()).difference(set(initialTypes))
for type in otherTypes:
self._outputType(type, **kArgs)
def log(self, **kArgs):
self._output(**kArgs)