moved EnforcesCalldowns to its own file, added getBase()

This commit is contained in:
Darren Ranalli 2006-10-03 20:41:37 +00:00
parent c6e1025459
commit 11521abf8a
4 changed files with 210 additions and 195 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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