Browse Source

moved EnforcesCalldowns to its own file, added getBase()

Darren Ranalli 19 years ago
parent
commit
11521abf8a

+ 1 - 1
direct/src/distributed/DistributedObject.py

@@ -3,7 +3,7 @@
 from pandac.PandaModules import *
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
-from direct.showbase.PythonUtil import EnforcesCalldowns, calldownEnforced
+from direct.showbase.EnforcesCalldowns import EnforcesCalldowns, calldownEnforced
 #from PyDatagram import PyDatagram
 #from PyDatagramIterator import PyDatagramIterator
 

+ 1 - 1
direct/src/distributed/DistributedObjectAI.py

@@ -3,7 +3,7 @@
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.distributed.DistributedObjectBase import DistributedObjectBase
 from direct.showbase import PythonUtil
-from direct.showbase.PythonUtil import EnforcesCalldowns, calldownEnforced
+from direct.showbase.EnforcesCalldowns import EnforcesCalldowns, calldownEnforced
 from pandac.PandaModules import *
 #from PyDatagram import PyDatagram
 #from PyDatagramIterator import PyDatagramIterator

+ 203 - 0
direct/src/showbase/EnforcesCalldowns.py

@@ -0,0 +1,203 @@
+__all__ = ['EnforcesCalldowns', 'calldownEnforced', 'EnforcedCalldownException',
+           ]
+
+from direct.showbase.PythonUtil import ClassTree, getBase
+import new
+
+class EnforcedCalldownException(Exception):
+    def __init__(self, what):
+        Exception.__init__(self, what)
+
+class EnforcesCalldowns:
+    """Derive from this class if you want to ensure that specific methods
+    get called.  See calldownEnforced decorator below"""
+
+    # class-level data for enforcement of base class method call-down
+    #
+    # The problem is that we don't have access to the class in the
+    # decorator, so we need to put the decorated methods in a global
+    # dict. We can then insert a stub method on each class instance for
+    # every method that has enforced base-class methods, and the stub can
+    # watch for each base-class method checkpoint to be passed.
+
+    # since the decorator can't know its own id until after it has been
+    # defined, we map from decorator ID to original func ID
+    _decoId2funcId = {}
+    # as calldownEnforced decorators are created, they add themselves to
+    # this dict. At this point we don't know what class they belong to.
+    _funcId2func = {}
+    # this is here so that we can print nice error messages
+    _funcId2class = {}
+    # as class instances are created, we populate this dictionary of
+    # class to func name to list of func ids. The lists of func ids
+    # include base-class funcs.
+    _class2funcName2funcIds = {}
+
+    # this method will be inserted into instances of classes that need
+    # to enforce base-class method calls, as the most-derived implementation
+    # of the method
+    @staticmethod
+    def _enforceCalldowns(oldMethod, name, obj, *args, **kArgs):
+        name2funcIds = EnforcesCalldowns._class2funcName2funcIds[obj.__class__]
+        funcIds = name2funcIds.get(name)
+
+        # prepare for the method call
+        for funcId in funcIds:
+            obj._EClatch(funcId)
+
+        # call the actual method that we're stubbing
+        result = oldMethod(*args, **kArgs)
+
+        # check on the results
+        for funcId in funcIds:
+            obj._ECcheck(funcId)
+
+        return result
+
+    @staticmethod
+    def notActive():
+        return not (__dev__ and getBase().config.GetBool('enforce-calldowns', 1))
+
+    def __init__(self):
+        if EnforcesCalldowns.notActive():
+            return
+
+        # this map tracks how many times each func has been called
+        self._funcId2calls = {}
+        # this map tracks the 'latch' values for each func; if the call count
+        # for a func is greater than the latch, then the func has been called.
+        self._funcId2latch = {}
+
+        if self.__class__ not in EnforcesCalldowns._class2funcName2funcIds:
+            # prepare stubs to enforce method call-downs
+            EnforcesCalldowns._class2funcName2funcIds.setdefault(self.__class__, {})
+            # look through all of our base classes and find matches
+            classes = ClassTree(self).getAllClasses()
+            # collect IDs of all the enforced methods
+            funcId2func = {}
+            for cls in classes:
+                for name, item in cls.__dict__.items():
+                    if id(item) in EnforcesCalldowns._decoId2funcId:
+                        funcId = EnforcesCalldowns._decoId2funcId[id(item)]
+                        funcId2func[funcId] = item
+                        EnforcesCalldowns._funcId2class[funcId] = cls
+            # add these funcs to the list for our class
+            funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
+            for funcId, func in funcId2func.items():
+                funcName2funcIds.setdefault(func.__name__, [])
+                funcName2funcIds[func.__name__].append(funcId)
+
+        # now run through all the enforced funcs for this class and insert
+        # stub methods to do the enforcement
+        funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
+        self._obscuredMethodNames = set()
+        for name in funcName2funcIds:
+            oldMethod = getattr(self, name)
+            self._obscuredMethodNames.add(name)
+            setattr(self, name, new.instancemethod(
+                Functor(EnforcesCalldowns._enforceCalldowns, oldMethod, name),
+                self, self.__class__))
+            
+    def EC_destroy(self):
+        """this used to be called destroy() but it was masking destroy() functions
+        on other classes that were multiply-inherited after ('to the right of')
+        this class"""
+        if EnforcesCalldowns.notActive():
+            return
+        # this must be called on destruction to prevent memory leaks
+        for name in self._obscuredMethodNames:
+            delattr(self, name)
+        del self._obscuredMethodNames
+        # this opens up more cans of worms. Let's keep it closed for the moment
+        #del self._funcId2calls
+        #del self._funcId2latch
+
+    def skipCalldown(self, method):
+        if EnforcesCalldowns.notActive():
+            return
+        # Call this function if you really don't want to call down to an
+        # enforced base-class method. This should hardly ever be used.
+        funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
+        funcIds = funcName2funcIds[method.__name__]
+        for funcId in funcIds:
+            self._ECvisit(funcId)
+
+    def _EClatch(self, funcId):
+        self._funcId2calls.setdefault(funcId, 0)
+        self._funcId2latch[funcId] = self._funcId2calls[funcId]
+    def _ECvisit(self, funcId):
+        self._funcId2calls.setdefault(funcId, 0)
+        self._funcId2calls[funcId] += 1
+    def _ECcheck(self, funcId):
+        if self._funcId2latch[funcId] == self._funcId2calls[funcId]:
+            func = EnforcesCalldowns._funcId2func[funcId]
+            raise EnforcedCalldownException(
+                '%s.%s did not call down to %s.%s\n%s' % (
+                self.__class__.__module__, self.__class__.__name__,
+                EnforcesCalldowns._funcId2class[funcId].__name__,
+                func.__name__,
+                ClassTree(self)))
+
+def calldownEnforced(f):
+    """
+    Use this decorator to ensure that derived classes that override this method
+    call down to the base class method.
+    """
+    if EnforcesCalldowns.notActive():
+        return f
+    def calldownEnforcedImpl(obj, *args, **kArgs):
+        # track the fact that this func has been called
+        obj._ECvisit(id(f))
+        f(obj, *args, **kArgs)
+    calldownEnforcedImpl.__doc__ = f.__doc__
+    calldownEnforcedImpl.__name__ = f.__name__
+    calldownEnforcedImpl.__module__ = f.__module__
+    EnforcesCalldowns._decoId2funcId[id(calldownEnforcedImpl)] = id(f)
+    EnforcesCalldowns._funcId2func[id(f)] = calldownEnforcedImpl
+    return calldownEnforcedImpl
+
+if not EnforcesCalldowns.notActive():
+    class CalldownEnforceTest(EnforcesCalldowns):
+        @calldownEnforced
+        def testFunc(self):
+            pass
+    class CalldownEnforceTestSubclass(CalldownEnforceTest):
+        def testFunc(self):
+            CalldownEnforceTest.testFunc(self)
+    class CalldownEnforceTestSubclassFail(CalldownEnforceTest):
+        def testFunc(self):
+            pass
+    class CalldownEnforceTestSubclassSkip(CalldownEnforceTest):
+        def testFunc(self):
+            self.skipCalldown(CalldownEnforceTest.testFunc)
+    cets = CalldownEnforceTestSubclass()
+    cetsf = CalldownEnforceTestSubclassFail()
+    cetss = CalldownEnforceTestSubclassSkip()
+    raised = False
+    try:
+        cets.testFunc()
+    except EnforcedCalldownException, e:
+        raised = True
+    if raised:
+        raise "calldownEnforced raised when it shouldn't"
+    raised = False
+    try:
+        cetsf.testFunc()
+    except EnforcedCalldownException, e:
+        raised = True
+    if not raised:
+        raise 'calldownEnforced failed to raise'
+    raised = False
+    try:
+        cetss.testFunc()
+    except EnforcedCalldownException, e:
+        raised = True
+    if raised:
+        raise "calldownEnforced.skipCalldown raised when it shouldn't"
+    del cetss
+    del cetsf
+    del cets
+    del CalldownEnforceTestSubclassSkip
+    del CalldownEnforceTestSubclassFail
+    del CalldownEnforceTestSubclass
+    del CalldownEnforceTest

+ 5 - 193
direct/src/showbase/PythonUtil.py

@@ -23,7 +23,7 @@ __all__ = ['enumerate', 'unique', 'indent', 'nonRepeatingRandomList',
 '_equal', '_notEqual', '_isNone', '_notNone', '_contains', '_notIn',
 'ScratchPad', 'Sync', 'RefCounter', 'itype', 'getNumberedTypedString',
 'printNumberedTyped', 'DelayedCall', 'DelayedFunctor',
-'FrameDelayedCallback', 'ArgumentEater']
+'FrameDelayedCallback', 'ArgumentEater', 'ClassTree', 'getBase',]
 
 import types
 import string
@@ -2369,199 +2369,11 @@ class ClassTree:
     def __repr__(self):
         return self._getStr()
 
-class EnforcedCalldownException(Exception):
-    def __init__(self, what):
-        Exception.__init__(self, what)
-
-class EnforcesCalldowns:
-    """Derive from this class if you want to ensure that specific methods
-    get called.  See calldownEnforced decorator below"""
-
-    # class-level data for enforcement of base class method call-down
-    #
-    # The problem is that we don't have access to the class in the
-    # decorator, so we need to put the decorated methods in a global
-    # dict. We can then insert a stub method on each class instance for
-    # every method that has enforced base-class methods, and the stub can
-    # watch for each base-class method checkpoint to be passed.
-
-    # since the decorator can't know its own id until after it has been
-    # defined, we map from decorator ID to original func ID
-    _decoId2funcId = {}
-    # as calldownEnforced decorators are created, they add themselves to
-    # this dict. At this point we don't know what class they belong to.
-    _funcId2func = {}
-    # this is here so that we can print nice error messages
-    _funcId2class = {}
-    # as class instances are created, we populate this dictionary of
-    # class to func name to list of func ids. The lists of func ids
-    # include base-class funcs.
-    _class2funcName2funcIds = {}
-
-    # this method will be inserted into instances of classes that need
-    # to enforce base-class method calls, as the most-derived implementation
-    # of the method
-    @staticmethod
-    def _enforceCalldowns(oldMethod, name, obj, *args, **kArgs):
-        name2funcIds = EnforcesCalldowns._class2funcName2funcIds[obj.__class__]
-        funcIds = name2funcIds.get(name)
-
-        # prepare for the method call
-        for funcId in funcIds:
-            obj._EClatch(funcId)
-
-        # call the actual method that we're stubbing
-        result = oldMethod(*args, **kArgs)
-
-        # check on the results
-        for funcId in funcIds:
-            obj._ECcheck(funcId)
-
-        return result
-
-    def __init__(self):
-        if not __debug__:
-            return
-
-        # this map tracks how many times each func has been called
-        self._funcId2calls = {}
-        # this map tracks the 'latch' values for each func; if the call count
-        # for a func is greater than the latch, then the func has been called.
-        self._funcId2latch = {}
-
-        if self.__class__ not in EnforcesCalldowns._class2funcName2funcIds:
-            # prepare stubs to enforce method call-downs
-            EnforcesCalldowns._class2funcName2funcIds.setdefault(self.__class__, {})
-            # look through all of our base classes and find matches
-            classes = ClassTree(self).getAllClasses()
-            # collect IDs of all the enforced methods
-            funcId2func = {}
-            for cls in classes:
-                for name, item in cls.__dict__.items():
-                    if id(item) in EnforcesCalldowns._decoId2funcId:
-                        funcId = EnforcesCalldowns._decoId2funcId[id(item)]
-                        funcId2func[funcId] = item
-                        EnforcesCalldowns._funcId2class[funcId] = cls
-            # add these funcs to the list for our class
-            funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
-            for funcId, func in funcId2func.items():
-                funcName2funcIds.setdefault(func.__name__, [])
-                funcName2funcIds[func.__name__].append(funcId)
-
-        # now run through all the enforced funcs for this class and insert
-        # stub methods to do the enforcement
-        funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
-        self._obscuredMethodNames = set()
-        for name in funcName2funcIds:
-            oldMethod = getattr(self, name)
-            self._obscuredMethodNames.add(name)
-            setattr(self, name, new.instancemethod(
-                Functor(EnforcesCalldowns._enforceCalldowns, oldMethod, name),
-                self, self.__class__))
-            
-    def EC_destroy(self):
-        """this used to be called destroy() but it was masking destroy() functions
-        on other classes that were multiply-inherited after ('to the right of')
-        this class"""
-        if not __debug__:
-            return
-        # this must be called on destruction to prevent memory leaks
-        for name in self._obscuredMethodNames:
-            delattr(self, name)
-        del self._obscuredMethodNames
-        # this opens up more cans of worms. Let's keep it closed for the moment
-        #del self._funcId2calls
-        #del self._funcId2latch
-
-    def skipCalldown(self, method):
-        if not __debug__:
-            return
-        # Call this function if you really don't want to call down to an
-        # enforced base-class method. This should hardly ever be used.
-        funcName2funcIds = EnforcesCalldowns._class2funcName2funcIds[self.__class__]
-        funcIds = funcName2funcIds[method.__name__]
-        for funcId in funcIds:
-            self._ECvisit(funcId)
-
-    def _EClatch(self, funcId):
-        self._funcId2calls.setdefault(funcId, 0)
-        self._funcId2latch[funcId] = self._funcId2calls[funcId]
-    def _ECvisit(self, funcId):
-        self._funcId2calls.setdefault(funcId, 0)
-        self._funcId2calls[funcId] += 1
-    def _ECcheck(self, funcId):
-        if self._funcId2latch[funcId] == self._funcId2calls[funcId]:
-            func = EnforcesCalldowns._funcId2func[funcId]
-            raise EnforcedCalldownException(
-                '%s.%s did not call down to %s.%s\n%s' % (
-                self.__class__.__module__, self.__class__.__name__,
-                EnforcesCalldowns._funcId2class[funcId].__name__,
-                func.__name__,
-                ClassTree(self)))
-
-def calldownEnforced(f):
-    """
-    Use this decorator to ensure that derived classes that override this method
-    call down to the base class method.
-    """
-    if not __debug__:
-        return f
-    def calldownEnforcedImpl(obj, *args, **kArgs):
-        # track the fact that this func has been called
-        obj._ECvisit(id(f))
-        f(obj, *args, **kArgs)
-    calldownEnforcedImpl.__doc__ = f.__doc__
-    calldownEnforcedImpl.__name__ = f.__name__
-    calldownEnforcedImpl.__module__ = f.__module__
-    EnforcesCalldowns._decoId2funcId[id(calldownEnforcedImpl)] = id(f)
-    EnforcesCalldowns._funcId2func[id(f)] = calldownEnforcedImpl
-    return calldownEnforcedImpl
-
-if __debug__:
-    class CalldownEnforceTest(EnforcesCalldowns):
-        @calldownEnforced
-        def testFunc(self):
-            pass
-    class CalldownEnforceTestSubclass(CalldownEnforceTest):
-        def testFunc(self):
-            CalldownEnforceTest.testFunc(self)
-    class CalldownEnforceTestSubclassFail(CalldownEnforceTest):
-        def testFunc(self):
-            pass
-    class CalldownEnforceTestSubclassSkip(CalldownEnforceTest):
-        def testFunc(self):
-            self.skipCalldown(CalldownEnforceTest.testFunc)
-    cets = CalldownEnforceTestSubclass()
-    cetsf = CalldownEnforceTestSubclassFail()
-    cetss = CalldownEnforceTestSubclassSkip()
-    raised = False
+def getBase():
     try:
-        cets.testFunc()
-    except EnforcedCalldownException, e:
-        raised = True
-    if raised:
-        raise "calldownEnforced raised when it shouldn't"
-    raised = False
-    try:
-        cetsf.testFunc()
-    except EnforcedCalldownException, e:
-        raised = True
-    if not raised:
-        raise 'calldownEnforced failed to raise'
-    raised = False
-    try:
-        cetss.testFunc()
-    except EnforcedCalldownException, e:
-        raised = True
-    if raised:
-        raise "calldownEnforced.skipCalldown raised when it shouldn't"
-    del cetss
-    del cetsf
-    del cets
-    del CalldownEnforceTestSubclassSkip
-    del CalldownEnforceTestSubclassFail
-    del CalldownEnforceTestSubclass
-    del CalldownEnforceTest
+        return base
+    except:
+        return simbase
 
 import __builtin__
 __builtin__.Functor = Functor