diff --git a/direct/src/showbase/ContainerLeakDetector.py b/direct/src/showbase/ContainerLeakDetector.py index ed60e75097..714c02283e 100755 --- a/direct/src/showbase/ContainerLeakDetector.py +++ b/direct/src/showbase/ContainerLeakDetector.py @@ -11,12 +11,15 @@ import weakref import random import builtins -deadEndTypes = (bool, types.BuiltinFunctionType, - types.BuiltinMethodType, complex, - float, int, - type(None), type(NotImplemented), - type, types.CodeType, types.FunctionType, - bytes, str, tuple) +deadEndTypes = frozenset(( + types.BuiltinFunctionType, types.BuiltinMethodType, + types.CodeType, types.FunctionType, + types.GeneratorType, types.CoroutineType, + types.AsyncGeneratorType, + bool, complex, float, int, type, + bytes, str, list, tuple, + type(None), type(NotImplemented) +)) def _createContainerLeak(): @@ -547,6 +550,23 @@ class FindContainers(Job): # if we hit a dead end, start over from another container curObjRef = None + # types.CellType was added in Python 3.8 + if sys.version_info >= (3, 8) and type(curObj) is types.CellType: + child = curObj.cell_contents + hasLength = self._hasLength(child) + notDeadEnd = not self._isDeadEnd(child) + if hasLength or notDeadEnd: + objRef = ObjectRef(Indirection(evalStr='.cell_contents'), + id(child), parentObjRef) + yield None + if hasLength: + for i in self._addContainerGen(child, objRef): + yield None + if notDeadEnd: + self._addDiscoveredStartRef(child, objRef) + curObjRef = objRef + continue + if hasattr(curObj, '__dict__'): child = curObj.__dict__ hasLength = self._hasLength(child) diff --git a/direct/src/showbase/ContainerReport.py b/direct/src/showbase/ContainerReport.py index 3a2cd768b2..7731fc4dd0 100755 --- a/direct/src/showbase/ContainerReport.py +++ b/direct/src/showbase/ContainerReport.py @@ -3,7 +3,10 @@ from direct.showbase.PythonUtil import Queue, invertDictLossless from direct.showbase.PythonUtil import safeRepr from direct.showbase.Job import Job from direct.showbase.JobManagerGlobal import jobMgr +from direct.showbase.ContainerLeakDetector import deadEndTypes import types +import sys +import io class ContainerReport(Job): @@ -89,14 +92,6 @@ class ContainerReport(Job): if isinstance(parentObj, (str, bytes)): 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 isinstance(parentObj, dict): key = None attr = None @@ -126,7 +121,25 @@ class ContainerReport(Job): del attr continue - if type(parentObj) is not types.FileType: + # types.CellType was added in Python 3.8 + if sys.version_info >= (3, 8) and type(parentObj) is types.CellType: + child = parentObj.cell_contents + if self._examine(child): + assert (self._queue.back() is child) + self._instanceDictIds.add(id(child)) + self._id2pathStr[id(child)] = str(self._id2pathStr[id(parentObj)]) + '.cell_contents' + continue + + if hasattr(parentObj, '__dict__'): + # Instance of a class + 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 not isinstance(parentObj, io.TextIOWrapper): try: itr = iter(parentObj) except: @@ -161,7 +174,10 @@ class ContainerReport(Job): childName = None child = None for childName in childNames: - child = getattr(parentObj, childName) + try: + child = getattr(parentObj, childName) + except: + continue if id(child) not in self._visitedIds: self._visitedIds.add(id(child)) if self._examine(child): @@ -198,9 +214,7 @@ class ContainerReport(Job): 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 (bool, types.BuiltinFunctionType, types.BuiltinMethodType, - complex, float, int, type(None), type(NotImplemented), - type, types.CodeType, types.FunctionType): + if type(obj) in deadEndTypes: return False # if it's an internal object, ignore it if id(obj) in ContainerReport.PrivateIds: @@ -243,7 +257,7 @@ class ContainerReport(Job): for i in self._outputType(type, **kArgs): yield None otherTypes = list(set(self._type2id2len.keys()).difference(set(initialTypes))) - otherTypes.sort() + otherTypes.sort(key=lambda obj: obj.__name__) for type in otherTypes: for i in self._outputType(type, **kArgs): yield None diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py old mode 100755 new mode 100644