|
|
@@ -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)
|