From 431d8555132975c0485c207a9140ac3d49beaa9e Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Fri, 13 Oct 2006 00:10:57 +0000 Subject: [PATCH] breadth-first search --- direct/src/showbase/ContainerReport.py | 192 +++++++++++++++++-------- 1 file changed, 136 insertions(+), 56 deletions(-) diff --git a/direct/src/showbase/ContainerReport.py b/direct/src/showbase/ContainerReport.py index dde3c3deca..ad74c31f64 100755 --- a/direct/src/showbase/ContainerReport.py +++ b/direct/src/showbase/ContainerReport.py @@ -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,65 +51,124 @@ 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:] == '__': - continue - attr = getattr(obj, name) + + if type(parentObj) in (types.StringType, types.UnicodeType): + continue + + 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(parentObj) + except: + pass + else: try: - itr = iter(obj) - except: + index = 0 + 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 - else: - try: - index = 0 - while 1: - try: - attr = itr.next() - except: - # some custom classes don't do well when iterated - 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) - index += 1 - except StopIteration, e: - pass - - def __repr__(self): - len2ids = invertDictLossless(self._id2len) + del itr + continue + + 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)