Browse Source

support for ObjectReport and non-garbage leak detection

Darren Ranalli 19 years ago
parent
commit
6f3e0b21ca
1 changed files with 150 additions and 4 deletions
  1. 150 4
      direct/src/showbase/PythonUtil.py

+ 150 - 4
direct/src/showbase/PythonUtil.py

@@ -8,6 +8,7 @@ import os
 import sys
 import sys
 import random
 import random
 import time
 import time
+import new
 if __debug__:
 if __debug__:
     import traceback
     import traceback
 
 
@@ -1559,16 +1560,16 @@ def lineInfo(baseFileName=1):
     (answers the question: 'hey lineInfo, where am I in the codebase?')
     (answers the question: 'hey lineInfo, where am I in the codebase?')
     see stackEntryInfo, above, for info on 'baseFileName' and return types
     see stackEntryInfo, above, for info on 'baseFileName' and return types
     """
     """
-    return stackEntryInfo(1)
+    return stackEntryInfo(1, baseFileName)
 
 
-def callerInfo(baseFileName=1):
+def callerInfo(baseFileName=1, howFarBack=0):
     """
     """
     returns the sourcefilename, line number, and function name of the
     returns the sourcefilename, line number, and function name of the
     caller of the function that called this function
     caller of the function that called this function
     (answers the question: 'hey callerInfo, who called me?')
     (answers the question: 'hey callerInfo, who called me?')
     see stackEntryInfo, above, for info on 'baseFileName' and return types
     see stackEntryInfo, above, for info on 'baseFileName' and return types
     """
     """
-    return stackEntryInfo(2)
+    return stackEntryInfo(2+howFarBack, baseFileName)
 
 
 def lineTag(baseFileName=1, verbose=0, separator=':'):
 def lineTag(baseFileName=1, verbose=0, separator=':'):
     """
     """
@@ -1584,7 +1585,7 @@ def lineTag(baseFileName=1, verbose=0, separator=':'):
 
 
     returns empty string on error
     returns empty string on error
     """
     """
-    fileName, lineNum, funcName = callerInfo()
+    fileName, lineNum, funcName = callerInfo(baseFileName)
     if fileName is None:
     if fileName is None:
         return ''
         return ''
     if verbose:
     if verbose:
@@ -1999,6 +2000,61 @@ def safeRepr(obj):
     except:
     except:
         return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
         return '<** FAILED REPR OF %s **>' % obj.__class__.__name__
 
 
+def fastRepr(obj, maxLen=200, strFactor=10):
+    """ caps the length of iterable types """
+    if type(obj) in (types.TupleType, types.ListType):
+        s = ''
+        s += {types.TupleType: '(',
+              types.ListType:  '[',}[type(obj)]
+        if len(obj) > maxLen:
+            o = obj[:maxLen]
+        else:
+            o = obj
+        for item in o:
+            s += fastRepr(item, maxLen)
+            s += ', '
+        s += '...'
+        s += {types.TupleType: ')',
+              types.ListType:  ']',}[type(obj)]
+        return s
+    elif type(obj) is types.DictType:
+        s = '{'
+        if len(obj) > maxLen:
+            o = obj.keys()[:maxLen]
+        else:
+            o = obj.keys()
+        for key in o:
+            value = obj[key]
+            s += '%s: %s, ' % (fastRepr(key, maxLen), fastRepr(value, maxLen))
+        s += '...}'
+        return s
+    elif type(obj) is types.StringType:
+        maxLen *= strFactor
+        if len(obj) > maxLen:
+            return obj[:maxLen]
+        else:
+            return obj
+    else:
+        return safeRepr(obj)
+
+def tagRepr(obj, tag):
+    """adds a string onto the repr output of an instance"""
+    def reprWithTag(oldRepr, tag, self):
+        return oldRepr() + '::<TAG=' + tag + '>'
+    oldRepr = getattr(obj, '__repr__', None)
+    if oldRepr is None:
+        def stringer(s):
+            return s
+        oldRepr = Functor(stringer, repr(obj))
+        stringer = None
+    obj.__repr__ = new.instancemethod(Functor(reprWithTag, oldRepr, tag), obj, obj.__class__)
+    reprWithTag = None
+    return obj
+
+def tagWithCaller(obj):
+    """add info about the caller of the caller"""
+    tagRepr(obj, str(callerInfo(howFarBack=1)))
+
 def isDefaultValue(x):
 def isDefaultValue(x):
     return x == type(x)()
     return x == type(x)()
 
 
@@ -2071,6 +2127,95 @@ class ScratchPad:
         for key, value in kArgs.items():
         for key, value in kArgs.items():
             setattr(self, key, value)
             setattr(self, key, value)
 
 
+class Sync:
+    _SeriesGen = SerialNumGen()
+    def __init__(self, name, other=None):
+        self._name = name
+        if other is None:
+            self._series = self._SeriesGen.next()
+            self._value = 0
+        else:
+            self._series = other._series
+            self._value = other._value
+    def invalidate(self):
+        self._value = None
+    def change(self):
+        self._value += 1
+    def sync(self, other):
+        if (self._series != other._series) or (self._value != other._value):
+            self._series = other._series
+            self._value = other._value
+            return True
+        else:
+            return False
+    def isSynced(self, other):
+        return ((self._series == other._series) and
+                (self._value == other._value))
+    def __repr__(self):
+        return '%s(%s)<family=%s,value=%s>' % (self.__class__.__name__,
+                              self._name, self._series, self._value)
+
+class RefCounter:
+    def __init__(self, byId=False):
+        self._byId = byId
+        self._refCounts = {}
+    def _getKey(self, item):
+        if self._byId:
+            key = id(item)
+        else:
+            key = item
+    def inc(self, item):
+        key = self._getKey(item)
+        self._refCounts.setdefault(key, 0)
+        self._refCounts[key] += 1
+    def dec(self, item):
+        """returns True if ref count has hit zero"""
+        key = self._getKey(item)
+        self._refCounts[key] -= 1
+        result = False
+        if self._refCounts[key] == 0:
+            result = True
+            del self._refCounts[key]
+        return result
+
+class VerboseInstanceType(types.InstanceType.__class__):
+    def __init__(self, obj):
+        types.InstanceType(self, obj)
+        self._reprStr = '%s of <class %s>' % (repr(types.InstanceType),
+                                            str(obj.__class__))
+    def __repr__(self):
+        return self._reprStr
+
+def itype(obj):
+    t = type(obj)
+    if t is types.InstanceType:
+        return VerboseInstanceType(obj)
+    else:
+        return t
+
+def getNumberedTypedString(items, maxLen=5000):
+    """get a string that has each item of the list on its own line,
+    and each item is numbered on the left from zero"""
+    digits = 0
+    n = len(items)
+    while n > 0:
+        digits += 1
+        n /= 10
+    digits = digits
+    format = '%0' + '%s' % digits + 'i:%s \t%s'
+    first = True
+    s = ''
+    for i in xrange(len(items)):
+        if not first:
+            s += '\n'
+        first = False
+        objStr = fastRepr(items[i])
+        if len(objStr) > maxLen:
+            snip = '<SNIP>'
+            objStr = '%s%s' % (objStr[:(maxLen-len(snip))], snip)
+        s += format % (i, itype(items[i]), objStr)
+    return s
+
 import __builtin__
 import __builtin__
 __builtin__.Functor = Functor
 __builtin__.Functor = Functor
 __builtin__.Stack = Stack
 __builtin__.Stack = Stack
@@ -2087,3 +2232,4 @@ __builtin__._isNone = _isNone
 __builtin__._notNone = _notNone
 __builtin__._notNone = _notNone
 __builtin__._contains = _contains
 __builtin__._contains = _contains
 __builtin__._notIn = _notIn
 __builtin__._notIn = _notIn
+__builtin__.itype = itype