Browse Source

breadth-first search

Darren Ranalli 19 years ago
parent
commit
431d855513
1 changed files with 136 additions and 56 deletions
  1. 136 56
      direct/src/showbase/ContainerReport.py

+ 136 - 56
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)