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 from direct.showbase.PythonUtil import itype, safeRepr
import types import types
"""
import types;from direct.showbase.ContainerReport import ContainerReport;ContainerReport().log()
"""
class ContainerReport: class ContainerReport:
def __init__(self, rootObj, rootObjName=None): PrivateIds = set()
self._rootObj = rootObj def __init__(self, name, log=False, limit=None):
if rootObjName is None: self._name = name
rootObjName = repr(rootObj)
self._visitedIds = set() self._visitedIds = set()
self._id2pathStr = {} self._id2pathStr = {}
self._id2container = {} self._id2container = {}
self._id2len = {} self._type2id2len = {}
self._stack = Stack() self._instanceDictIds = set()
self._stack.push(rootObj) # for breadth-first searching
self._id2pathStr[id(rootObj)] = rootObjName 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() self._traverse()
if log:
self.log(limit=limit)
def _examine(self, obj): def _examine(self, obj):
# return False if it's an object that can't contain or lead to other objects # return False if it's an object that can't contain or lead to other objects
if type(obj) in (types.BooleanType, types.BuiltinFunctionType, if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
types.BuiltinMethodType, types.ComplexType, types.BuiltinMethodType, types.ComplexType,
types.FloatType, types.IntType, types.LongType, types.FloatType, types.IntType, types.LongType,
types.NoneType, types.NotImplementedType, types.NoneType, types.NotImplementedType,
types.TypeType, types.CodeType): types.TypeType, types.CodeType, types.FunctionType):
return False 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: try:
length = len(obj) length = len(obj)
except: except:
@ -30,65 +51,124 @@ class ContainerReport:
if length is not None and length > 0: if length is not None and length > 0:
objId = id(obj) objId = id(obj)
self._id2container[objId] = obj self._id2container[objId] = obj
self._id2len[objId] = length self._type2id2len.setdefault(type(obj), {})
self._type2id2len[type(obj)][objId] = length
return True return True
def _traverse(self): def _traverse(self):
while len(self._stack) > 0: while len(self._queue) > 0:
obj = self._stack.pop() parentObj = self._queue.pop()
#print '_traverse: %s' % fastRepr(obj, 30) #print '%s: %s, %s' % (id(parentObj), type(parentObj), fastRepr(parentObj))
isInstanceDict = False
if id(parentObj) in self._instanceDictIds:
isInstanceDict = True
try: try:
dirList = dir(obj) if parentObj.__class__.__name__ == 'method-wrapper':
continue
except: except:
pass pass
else:
for name in dirList: if type(parentObj) in (types.StringType, types.UnicodeType):
if name[-2:] == '__': continue
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: if id(attr) not in self._visitedIds:
self._visitedIds.add(id(attr)) self._visitedIds.add(id(attr))
if self._examine(attr): if self._examine(attr):
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '.%s' % name assert _equal(self._queue.back(), attr)
self._stack.push(attr) if parentObj is __builtins__:
if type(obj) not in (types.StringType, types.UnicodeType): self._id2pathStr[id(attr)] = key
if type(obj) is types.DictType: else:
keys = obj.keys() if isInstanceDict:
for key in keys: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '.%s' % key
attr = obj[key] else:
if id(attr) not in self._visitedIds: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % safeRepr(key)
self._visitedIds.add(id(attr)) del key
if self._examine(attr): del attr
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '[%s]' % safeRepr(key) continue
self._stack.push(attr)
elif type(obj) is not types.FileType: if type(parentObj) is not types.FileType:
try:
itr = iter(parentObj)
except:
pass
else:
try: try:
itr = iter(obj) index = 0
except: while 1:
try:
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):
assert _equal(self._queue.back(), attr)
self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % index
index += 1
del attr
except StopIteration, e:
pass pass
else: del itr
try: continue
index = 0
while 1: try:
try: childNames = dir(parentObj)
attr = itr.next() except:
except: pass
# some custom classes don't do well when iterated else:
break childName = None
if id(attr) not in self._visitedIds: child = None
self._visitedIds.add(id(attr)) for childName in childNames:
if self._examine(attr): child = getattr(parentObj, childName)
self._id2pathStr[id(attr)] = self._id2pathStr[id(obj)] + '[%s]' % index if id(child) not in self._visitedIds:
self._stack.push(attr) self._visitedIds.add(id(child))
index += 1 if self._examine(child):
except StopIteration, e: assert _equal(self._queue.back(), child)
pass self._id2pathStr[id(child)] = self._id2pathStr[id(parentObj)] + '.%s' % childName
del childName
def __repr__(self): del child
len2ids = invertDictLossless(self._id2len) continue
def _outputType(self, type, limit=None):
if type not in self._type2id2len:
return
len2ids = invertDictLossless(self._type2id2len[type])
lengths = len2ids.keys() lengths = len2ids.keys()
lengths.sort() lengths.sort()
lengths.reverse() lengths.reverse()
print '====='
print '===== %s' % type
count = 0
stop = False
for l in lengths: for l in lengths:
for id in len2ids[l]: for id in len2ids[l]:
obj = self._id2container[id] 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)