| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- from direct.directnotify.DirectNotifyGlobal import directNotify
- from direct.showbase.PythonUtil import Queue, invertDictLossless
- from direct.showbase.PythonUtil import safeRepr
- from direct.showbase.Job import Job
- import types
- class ContainerReport(Job):
- notify = directNotify.newCategory("ContainerReport")
- # set of containers that should not be included in the report
- PrivateIds = set()
- def __init__(self, name, log=False, limit=None, threaded=False):
- Job.__init__(self, name)
- self._log = log
- self._limit = limit
- # set up our data structures
- self._visitedIds = set()
- self._id2pathStr = {}
- self._id2container = {}
- self._type2id2len = {}
- self._instanceDictIds = set()
- # for breadth-first searching
- self._queue = Queue()
- jobMgr.add(self)
- if threaded == False:
- jobMgr.finish(self)
- def destroy(self):
- del self._queue
- del self._instanceDictIds
- del self._type2id2len
- del self._id2container
- del self._id2pathStr
- del self._visitedIds
- del self._limit
- del self._log
- def finished(self):
- if self._log:
- self.destroy()
- def run(self):
- ContainerReport.PrivateIds.update(set([
- id(ContainerReport.PrivateIds),
- id(self._visitedIds),
- id(self._id2pathStr),
- id(self._id2container),
- id(self._type2id2len),
- id(self._queue),
- id(self._instanceDictIds),
- ]))
- # push on a few things that we want to give priority
- # for the sake of the variable-name printouts
- try:
- base
- except:
- pass
- else:
- self._enqueueContainer( base.__dict__,
- 'base')
- try:
- simbase
- except:
- pass
- else:
- self._enqueueContainer( simbase.__dict__,
- 'simbase')
- self._queue.push(__builtins__)
- self._id2pathStr[id(__builtins__)] = ''
- while len(self._queue) > 0:
- # yield up here instead of at the end, since we skip back to the
- # top of the while loop from various points
- yield None
- parentObj = self._queue.pop()
- #print '%s: %s, %s' % (id(parentObj), type(parentObj), self._id2pathStr[id(parentObj)])
- isInstanceDict = False
- if id(parentObj) in self._instanceDictIds:
- isInstanceDict = True
- try:
- if parentObj.__class__.__name__ == 'method-wrapper':
- continue
- except:
- pass
- 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 (self._queue.back() is 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
- keys = parentObj.keys()
- try:
- keys.sort()
- except TypeError, e:
- self.notify.warning('non-sortable dict keys: %s: %s' % (self._id2pathStr[id(parentObj)], repr(e)))
- for key in keys:
- try:
- attr = parentObj[key]
- except KeyError, e:
- self.notify.warning('could not index into %s with key %s' % (self._id2pathStr[id(parentObj)],
- key))
- if id(attr) not in self._visitedIds:
- self._visitedIds.add(id(attr))
- if self._examine(attr):
- assert (self._queue.back() is 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:
- 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 (self._queue.back() is attr)
- self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % index
- index += 1
- del attr
- except StopIteration, e:
- pass
- 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 (self._queue.back() is child)
- self._id2pathStr[id(child)] = self._id2pathStr[id(parentObj)] + '.%s' % childName
- del childName
- del child
- continue
- if self._log:
- self.printingBegin()
- for i in self._output(limit=self._limit):
- yield None
- self.printingEnd()
- yield Job.Done
-
- def _enqueueContainer(self, obj, pathStr=None):
- # call this to add a container that should be examined before any (other) direct
- # children of __builtins__
- # this is mostly to fix up the names of variables
- self._queue.push(obj)
- objId = id(obj)
- if pathStr is not None:
- self._id2pathStr[objId] = pathStr
- # if it's a container, put it in the tables
- try:
- length = len(obj)
- except:
- length = None
- if length is not None and length > 0:
- self._id2container[objId] = obj
- self._type2id2len.setdefault(type(obj), {})
- self._type2id2len[type(obj)][objId] = length
- 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.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._enqueueContainer(obj)
- return True
- 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:
- #len2ids[l].sort()
- pathStrList = list()
- for id in len2ids[l]:
- obj = self._id2container[id]
- #print '%s: %s' % (l, self._id2pathStr[id])
- pathStrList.append(self._id2pathStr[id])
- count += 1
- if (count & 0x7f) == 0:
- yield None
- pathStrList.sort()
- for pathstr in pathStrList:
- print '%s: %s' % (l, pathstr)
- if limit is not None and count >= limit:
- return
- def _output(self, **kArgs):
- print "===== ContainerReport: \'%s\' =====" % (self._name,)
- initialTypes = (types.DictType, types.ListType, types.TupleType)
- for type in initialTypes:
- for i in self._outputType(type, **kArgs):
- yield None
- otherTypes = list(set(self._type2id2len.keys()).difference(set(initialTypes)))
- otherTypes.sort()
- for type in otherTypes:
- for i in self._outputType(type, **kArgs):
- yield None
- def log(self, **kArgs):
- self._output(**kArgs)
|