removed EnforcesCalldowns

This commit is contained in:
Darren Ranalli 2008-01-10 00:06:47 +00:00
parent 9b4a1eab47
commit 59fe42a254
3 changed files with 2 additions and 275 deletions

View File

@ -3,7 +3,6 @@
from pandac.PandaModules import * from pandac.PandaModules import *
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.distributed.DistributedObjectBase import DistributedObjectBase from direct.distributed.DistributedObjectBase import DistributedObjectBase
from direct.showbase.EnforcesCalldowns import EnforcesCalldowns, calldownEnforced
#from PyDatagram import PyDatagram #from PyDatagram import PyDatagram
#from PyDatagramIterator import PyDatagramIterator #from PyDatagramIterator import PyDatagramIterator
@ -16,7 +15,7 @@ ESDisabled = 4 # values here and lower are considered "disabled"
ESGenerating = 5 # values here and greater are considered "generated" ESGenerating = 5 # values here and greater are considered "generated"
ESGenerated = 6 ESGenerated = 6
class DistributedObject(DistributedObjectBase, EnforcesCalldowns): class DistributedObject(DistributedObjectBase):
""" """
The Distributed Object class is the base class for all network based The Distributed Object class is the base class for all network based
(i.e. distributed) objects. These will usually (always?) have a (i.e. distributed) objects. These will usually (always?) have a
@ -38,8 +37,6 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
self.DistributedObject_initialized = 1 self.DistributedObject_initialized = 1
DistributedObjectBase.__init__(self, cr) DistributedObjectBase.__init__(self, cr)
EnforcesCalldowns.__init__(self)
# Most DistributedObjects are simple and require no real # Most DistributedObjects are simple and require no real
# effort to load. Some, particularly actors, may take # effort to load. Some, particularly actors, may take
# some significant time to load; these we can optimize by # some significant time to load; these we can optimize by
@ -203,7 +200,6 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
messenger.send(self.getDisableEvent()) messenger.send(self.getDisableEvent())
self.disable() self.disable()
@calldownEnforced
def announceGenerate(self): def announceGenerate(self):
""" """
Sends a message to the world after the object has been Sends a message to the world after the object has been
@ -212,7 +208,6 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
assert self.notify.debug('announceGenerate(): %s' % (self.doId)) assert self.notify.debug('announceGenerate(): %s' % (self.doId))
@calldownEnforced
def disable(self): def disable(self):
""" """
Inheritors should redefine this to take appropriate action on disable Inheritors should redefine this to take appropriate action on disable
@ -241,7 +236,6 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
assert self.notify.debugStateCall(self) assert self.notify.debugStateCall(self)
return (self.activeState == ESGenerated) return (self.activeState == ESGenerated)
@calldownEnforced
def delete(self): def delete(self):
""" """
Inheritors should redefine this to take appropriate action on delete Inheritors should redefine this to take appropriate action on delete
@ -253,9 +247,7 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
self.DistributedObject_deleted = 1 self.DistributedObject_deleted = 1
self.cr = None self.cr = None
self.dclass = None self.dclass = None
EnforcesCalldowns.EC_destroy(self)
@calldownEnforced
def generate(self): def generate(self):
""" """
Inheritors should redefine this to take appropriate action on generate Inheritors should redefine this to take appropriate action on generate
@ -268,7 +260,6 @@ class DistributedObject(DistributedObjectBase, EnforcesCalldowns):
if not hasattr(self, '_autoInterestHandle'): if not hasattr(self, '_autoInterestHandle'):
self.cr.openAutoInterests(self) self.cr.openAutoInterests(self)
@calldownEnforced
def generateInit(self): def generateInit(self):
""" """
This method is called when the DistributedObject is first introduced This method is called when the DistributedObject is first introduced

View File

@ -3,13 +3,12 @@
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.distributed.DistributedObjectBase import DistributedObjectBase from direct.distributed.DistributedObjectBase import DistributedObjectBase
from direct.showbase import PythonUtil from direct.showbase import PythonUtil
from direct.showbase.EnforcesCalldowns import EnforcesCalldowns, calldownEnforced
from otp.ai.AIZoneData import AIZoneData from otp.ai.AIZoneData import AIZoneData
from pandac.PandaModules import * from pandac.PandaModules import *
#from PyDatagram import PyDatagram #from PyDatagram import PyDatagram
#from PyDatagramIterator import PyDatagramIterator #from PyDatagramIterator import PyDatagramIterator
class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns): class DistributedObjectAI(DistributedObjectBase):
notify = directNotify.newCategory("DistributedObjectAI") notify = directNotify.newCategory("DistributedObjectAI")
QuietZone = 1 QuietZone = 1
@ -20,8 +19,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
self.DistributedObjectAI_initialized = 1 self.DistributedObjectAI_initialized = 1
DistributedObjectBase.__init__(self, air) DistributedObjectBase.__init__(self, air)
EnforcesCalldowns.__init__(self)
self.accountName='' self.accountName=''
# Record the repository # Record the repository
self.air = air self.air = air
@ -90,7 +87,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
if delEvent: if delEvent:
messenger.send(delEvent) messenger.send(delEvent)
@calldownEnforced
def delete(self): def delete(self):
""" """
Inheritors should redefine this to take appropriate action on delete Inheritors should redefine this to take appropriate action on delete
@ -150,8 +146,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
del self.zoneId del self.zoneId
self.__generated = False self.__generated = False
EnforcesCalldowns.EC_destroy(self)
def isDeleted(self): def isDeleted(self):
""" """
Returns true if the object has been deleted, Returns true if the object has been deleted,
@ -180,7 +174,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
self.doId = self.air.allocateChannel() self.doId = self.air.allocateChannel()
self.__preallocDoId = 1 self.__preallocDoId = 1
@calldownEnforced
def announceGenerate(self): def announceGenerate(self):
""" """
Called after the object has been generated and all Called after the object has been generated and all
@ -416,7 +409,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
self.announceGenerate() self.announceGenerate()
self.postGenerateMessage() self.postGenerateMessage()
@calldownEnforced
def generate(self): def generate(self):
""" """
Inheritors should put functions that require self.zoneId or Inheritors should put functions that require self.zoneId or
@ -425,7 +417,6 @@ class DistributedObjectAI(DistributedObjectBase, EnforcesCalldowns):
assert self.notify.debugStateCall(self) assert self.notify.debugStateCall(self)
@calldownEnforced
def generateInit(self, repository=None): def generateInit(self, repository=None):
""" """
First generate (not from cache). First generate (not from cache).

View File

@ -1,255 +0,0 @@
__all__ = ['EnforcesCalldowns', 'calldownEnforced', 'EnforcedCalldownException',
]
from direct.showbase.PythonUtil import ClassTree, getBase
import new, __builtin__
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
# protect against multiple calldowns via multiple inheritance
if hasattr(self, '_numInits'):
self._numInits += 1
else:
self._numInits = 1
# 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
# protect against multiple calldowns via multiple inheritance
assert hasattr(self, '_numInits'), (
'too many calls to EnforcesCalldowns.EC_destroy, class=%s' % self.__class__.__name__)
if self._numInits == 1:
for name in self._obscuredMethodNames:
# Functors need to be destroyed to prevent garbage leaks
getattr(self, name).destroy()
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
del self._numInits
else:
self._numInits -= 1
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]
__builtin__.classTree = ClassTree(self)
raise EnforcedCalldownException(
'%s.%s did not call down to %s.%s; type \'classTree\' to see hierarchy' % (
self.__class__.__module__, self.__class__.__name__,
EnforcesCalldowns._funcId2class[funcId].__name__,
func.__name__))
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 calldownEnforcedDecorator(obj, *args, **kArgs):
# track the fact that this func has been called
obj._ECvisit(id(f))
f(obj, *args, **kArgs)
calldownEnforcedDecorator.__doc__ = f.__doc__
calldownEnforcedDecorator.__name__ = f.__name__
calldownEnforcedDecorator.__module__ = f.__module__
EnforcesCalldowns._decoId2funcId[id(calldownEnforcedDecorator)] = id(f)
EnforcesCalldowns._funcId2func[id(f)] = calldownEnforcedDecorator
return calldownEnforcedDecorator
if not EnforcesCalldowns.notActive():
class CalldownEnforceTest(EnforcesCalldowns):
@calldownEnforced
def testFunc(self):
pass
class CalldownEnforceTestSubclass(CalldownEnforceTest):
def testFunc(self):
CalldownEnforceTest.testFunc(self)
def destroy(self):
CalldownEnforceTest.EC_destroy(self)
class CalldownEnforceTestSubclassFail(CalldownEnforceTest):
def testFunc(self):
pass
class CalldownEnforceTestSubclassSkip(CalldownEnforceTest):
def testFunc(self):
self.skipCalldown(CalldownEnforceTest.testFunc)
class CalldownEnforceTestSubclass2(CalldownEnforceTest):
def testFunc(self):
CalldownEnforceTest.testFunc(self)
def destroy(self):
CalldownEnforceTest.EC_destroy(self)
class CalldownEnforceTestDiamond(CalldownEnforceTestSubclass,
CalldownEnforceTestSubclass2):
def __init__(self):
CalldownEnforceTestSubclass.__init__(self)
CalldownEnforceTestSubclass2.__init__(self)
def testFunc(self):
CalldownEnforceTestSubclass.testFunc(self)
CalldownEnforceTestSubclass2.testFunc(self)
def destroy(self):
CalldownEnforceTestSubclass.destroy(self)
CalldownEnforceTestSubclass2.destroy(self)
cets = CalldownEnforceTestSubclass()
cetsf = CalldownEnforceTestSubclassFail()
cetss = CalldownEnforceTestSubclassSkip()
cetd = CalldownEnforceTestDiamond()
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"
raised = False
cetd.testFunc()
msg = ''
try:
# make sure we're OK to call down to destroy multiple times
cetd.destroy()
except Exception, e:
msg = str(e)
raised = True
if raised:
raise "calldownEnforcedDiamond.destroy raised when it shouldn't\n%s" % msg
cetss.EC_destroy()
cetsf.EC_destroy()
cets.EC_destroy()
del cetd
del cetss
del cetsf
del cets
del CalldownEnforceTestDiamond
del CalldownEnforceTestSubclass2
del CalldownEnforceTestSubclassSkip
del CalldownEnforceTestSubclassFail
del CalldownEnforceTestSubclass
del CalldownEnforceTest