"""Contains the ObjectPool utility class."""
__all__ = ['Diff', 'ObjectPool']
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import invertDictLossless, makeList, safeRepr, itype
from direct.showbase.PythonUtil import getNumberedTypedString, getNumberedTypedSortedString
import gc
[docs]class Diff:
[docs] def __init__(self, lost, gained):
self.lost = lost
self.gained = gained
[docs] def printOut(self, full=False):
print('lost %s objects, gained %s objects' % (len(self.lost), len(self.gained)))
print('\n\nself.lost\n')
print(self.lost.typeFreqStr())
print('\n\nself.gained\n')
print(self.gained.typeFreqStr())
if full:
self.gained.printObjsByType()
print('\n\nGAINED-OBJECT REFERRERS\n')
self.gained.printReferrers(1)
[docs]class ObjectPool:
"""manipulate a pool of Python objects"""
notify = directNotify.newCategory('ObjectPool')
[docs] def __init__(self, objects):
self._objs = list(objects)
self._type2objs = {}
self._count2types = {}
self._len2obj = {}
type2count = {}
for obj in self._objs:
typ = itype(obj)
type2count.setdefault(typ, 0)
type2count[typ] += 1
self._type2objs.setdefault(typ, [])
self._type2objs[typ].append(obj)
try:
self._len2obj[len(obj)] = obj
except:
pass
self._count2types = invertDictLossless(type2count)
def _getInternalObjs(self):
return (self._objs, self._type2objs, self._count2types)
[docs] def destroy(self):
del self._objs
del self._type2objs
del self._count2types
[docs] def getTypes(self):
return list(self._type2objs.keys())
[docs] def getObjsOfType(self, type):
return self._type2objs.get(type, [])
[docs] def printObjsOfType(self, type):
for obj in self._type2objs.get(type, []):
print(repr(obj))
[docs] def diff(self, other):
"""print difference between this pool and 'other' pool"""
thisId2obj = {}
otherId2obj = {}
for obj in self._objs:
thisId2obj[id(obj)] = obj
for obj in other._objs:
otherId2obj[id(obj)] = obj
thisIds = set(thisId2obj.keys())
otherIds = set(otherId2obj.keys())
lostIds = thisIds.difference(otherIds)
gainedIds = otherIds.difference(thisIds)
del thisIds
del otherIds
lostObjs = []
for i in lostIds:
lostObjs.append(thisId2obj[i])
gainedObjs = []
for i in gainedIds:
gainedObjs.append(otherId2obj[i])
return Diff(self.__class__(lostObjs), self.__class__(gainedObjs))
[docs] def typeFreqStr(self):
s = 'Object Pool: Type Frequencies'
s += '\n============================='
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
s += '\n%s\t%s' % (count, typ)
return s
[docs] def printObjsByType(self):
print('Object Pool: Objects By Type')
print('\n============================')
counts = list(set(self._count2types.keys()))
counts.sort()
# print types with the smallest number of instances first, in case
# there's a large group that waits a long time before printing
#counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
print('TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])))
print(getNumberedTypedSortedString(self._type2objs[typ]))
[docs] def printReferrers(self, numEach=3):
"""referrers of the first few of each type of object"""
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
print('\n\nTYPE: %s' % repr(typ))
for i in range(min(numEach, len(self._type2objs[typ]))):
obj = self._type2objs[typ][i]
print('\nOBJ: %s\n' % safeRepr(obj))
referrers = gc.get_referrers(obj)
print('%s REFERRERS:\n' % len(referrers))
if len(referrers) > 0:
print(getNumberedTypedString(referrers, maxLen=80,
numPrefix='REF'))
else:
print('<No Referrers>')
def __len__(self):
return len(self._objs)