ContainerReport.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. from direct.directnotify.DirectNotifyGlobal import directNotify
  2. from direct.showbase.PythonUtil import Queue, invertDictLossless
  3. from direct.showbase.PythonUtil import safeRepr
  4. from direct.showbase.Job import Job
  5. import types
  6. class ContainerReport(Job):
  7. notify = directNotify.newCategory("ContainerReport")
  8. # set of containers that should not be included in the report
  9. PrivateIds = set()
  10. def __init__(self, name, log=False, limit=None, threaded=False):
  11. Job.__init__(self, name)
  12. self._log = log
  13. self._limit = limit
  14. # set up our data structures
  15. self._visitedIds = set()
  16. self._id2pathStr = {}
  17. self._id2container = {}
  18. self._type2id2len = {}
  19. self._instanceDictIds = set()
  20. # for breadth-first searching
  21. self._queue = Queue()
  22. jobMgr.add(self)
  23. if threaded == False:
  24. jobMgr.finish(self)
  25. def destroy(self):
  26. del self._queue
  27. del self._instanceDictIds
  28. del self._type2id2len
  29. del self._id2container
  30. del self._id2pathStr
  31. del self._visitedIds
  32. del self._limit
  33. del self._log
  34. def finished(self):
  35. if self._log:
  36. self.destroy()
  37. def run(self):
  38. ContainerReport.PrivateIds.update(set([
  39. id(ContainerReport.PrivateIds),
  40. id(self._visitedIds),
  41. id(self._id2pathStr),
  42. id(self._id2container),
  43. id(self._type2id2len),
  44. id(self._queue),
  45. id(self._instanceDictIds),
  46. ]))
  47. # push on a few things that we want to give priority
  48. # for the sake of the variable-name printouts
  49. try:
  50. base
  51. except:
  52. pass
  53. else:
  54. self._enqueueContainer( base.__dict__,
  55. 'base')
  56. try:
  57. simbase
  58. except:
  59. pass
  60. else:
  61. self._enqueueContainer( simbase.__dict__,
  62. 'simbase')
  63. self._queue.push(__builtins__)
  64. self._id2pathStr[id(__builtins__)] = ''
  65. while len(self._queue) > 0:
  66. # yield up here instead of at the end, since we skip back to the
  67. # top of the while loop from various points
  68. yield None
  69. parentObj = self._queue.pop()
  70. #print '%s: %s, %s' % (id(parentObj), type(parentObj), self._id2pathStr[id(parentObj)])
  71. isInstanceDict = False
  72. if id(parentObj) in self._instanceDictIds:
  73. isInstanceDict = True
  74. try:
  75. if parentObj.__class__.__name__ == 'method-wrapper':
  76. continue
  77. except:
  78. pass
  79. if type(parentObj) in (types.StringType, types.UnicodeType):
  80. continue
  81. if type(parentObj) in (types.ModuleType, types.InstanceType):
  82. child = parentObj.__dict__
  83. if self._examine(child):
  84. assert (self._queue.back() is child)
  85. self._instanceDictIds.add(id(child))
  86. self._id2pathStr[id(child)] = str(self._id2pathStr[id(parentObj)])
  87. continue
  88. if type(parentObj) is types.DictType:
  89. key = None
  90. attr = None
  91. keys = parentObj.keys()
  92. try:
  93. keys.sort()
  94. except TypeError, e:
  95. self.notify.warning('non-sortable dict keys: %s: %s' % (self._id2pathStr[id(parentObj)], repr(e)))
  96. for key in keys:
  97. try:
  98. attr = parentObj[key]
  99. except KeyError, e:
  100. self.notify.warning('could not index into %s with key %s' % (self._id2pathStr[id(parentObj)],
  101. key))
  102. if id(attr) not in self._visitedIds:
  103. self._visitedIds.add(id(attr))
  104. if self._examine(attr):
  105. assert (self._queue.back() is attr)
  106. if parentObj is __builtins__:
  107. self._id2pathStr[id(attr)] = key
  108. else:
  109. if isInstanceDict:
  110. self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '.%s' % key
  111. else:
  112. self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % safeRepr(key)
  113. del key
  114. del attr
  115. continue
  116. if type(parentObj) is not types.FileType:
  117. try:
  118. itr = iter(parentObj)
  119. except:
  120. pass
  121. else:
  122. try:
  123. index = 0
  124. while 1:
  125. try:
  126. attr = itr.next()
  127. except:
  128. # some custom classes don't do well when iterated
  129. attr = None
  130. break
  131. if id(attr) not in self._visitedIds:
  132. self._visitedIds.add(id(attr))
  133. if self._examine(attr):
  134. assert (self._queue.back() is attr)
  135. self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % index
  136. index += 1
  137. del attr
  138. except StopIteration, e:
  139. pass
  140. del itr
  141. continue
  142. try:
  143. childNames = dir(parentObj)
  144. except:
  145. pass
  146. else:
  147. childName = None
  148. child = None
  149. for childName in childNames:
  150. child = getattr(parentObj, childName)
  151. if id(child) not in self._visitedIds:
  152. self._visitedIds.add(id(child))
  153. if self._examine(child):
  154. assert (self._queue.back() is child)
  155. self._id2pathStr[id(child)] = self._id2pathStr[id(parentObj)] + '.%s' % childName
  156. del childName
  157. del child
  158. continue
  159. if self._log:
  160. self.printingBegin()
  161. for i in self._output(limit=self._limit):
  162. yield None
  163. self.printingEnd()
  164. yield Job.Done
  165. def _enqueueContainer(self, obj, pathStr=None):
  166. # call this to add a container that should be examined before any (other) direct
  167. # children of __builtins__
  168. # this is mostly to fix up the names of variables
  169. self._queue.push(obj)
  170. objId = id(obj)
  171. if pathStr is not None:
  172. self._id2pathStr[objId] = pathStr
  173. # if it's a container, put it in the tables
  174. try:
  175. length = len(obj)
  176. except:
  177. length = None
  178. if length is not None and length > 0:
  179. self._id2container[objId] = obj
  180. self._type2id2len.setdefault(type(obj), {})
  181. self._type2id2len[type(obj)][objId] = length
  182. def _examine(self, obj):
  183. # return False if it's an object that can't contain or lead to other objects
  184. if type(obj) in (types.BooleanType, types.BuiltinFunctionType,
  185. types.BuiltinMethodType, types.ComplexType,
  186. types.FloatType, types.IntType, types.LongType,
  187. types.NoneType, types.NotImplementedType,
  188. types.TypeType, types.CodeType, types.FunctionType):
  189. return False
  190. # if it's an internal object, ignore it
  191. if id(obj) in ContainerReport.PrivateIds:
  192. return False
  193. # this object might lead to more objects. put it on the queue
  194. self._enqueueContainer(obj)
  195. return True
  196. def _outputType(self, type, limit=None):
  197. if type not in self._type2id2len:
  198. return
  199. len2ids = invertDictLossless(self._type2id2len[type])
  200. lengths = len2ids.keys()
  201. lengths.sort()
  202. lengths.reverse()
  203. print '====='
  204. print '===== %s' % type
  205. count = 0
  206. stop = False
  207. for l in lengths:
  208. #len2ids[l].sort()
  209. pathStrList = list()
  210. for id in len2ids[l]:
  211. obj = self._id2container[id]
  212. #print '%s: %s' % (l, self._id2pathStr[id])
  213. pathStrList.append(self._id2pathStr[id])
  214. count += 1
  215. if (count & 0x7f) == 0:
  216. yield None
  217. pathStrList.sort()
  218. for pathstr in pathStrList:
  219. print '%s: %s' % (l, pathstr)
  220. if limit is not None and count >= limit:
  221. return
  222. def _output(self, **kArgs):
  223. print "===== ContainerReport: \'%s\' =====" % (self._name,)
  224. initialTypes = (types.DictType, types.ListType, types.TupleType)
  225. for type in initialTypes:
  226. for i in self._outputType(type, **kArgs):
  227. yield None
  228. otherTypes = list(set(self._type2id2len.keys()).difference(set(initialTypes)))
  229. otherTypes.sort()
  230. for type in otherTypes:
  231. for i in self._outputType(type, **kArgs):
  232. yield None
  233. def log(self, **kArgs):
  234. self._output(**kArgs)