Changeset 100
- Timestamp:
- 11/13/07 17:11:03 (1 year ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
projects/Twisted-Goodies/trunk/twisted_goodies/pybywire/params.py
r97 r100 25 25 """ 26 26 27 import new28 27 from twisted.spread import pb 28 from twisted.spread.jelly import Jellyable, Unjellyable 29 29 30 30 31 class LocalVersion(pb.Copyable): 32 """ 33 I am a version of something parameterized that can be copied to a remote 34 node. 35 """ 36 def getStateToCopy(self): 37 state = {} 38 for sequence in (self.keyAttrs.keys(), self.paramNames): 39 for name in sequence: 40 if hasattr(self, name): 41 state[name] = getattr(self, name) 42 return state 43 44 45 class RemoteVersion(pb.RemoteCopy): 46 """ 47 I am a version of something parameterized that is instantiated at a remote 48 node. I override the constructor for L{Parameterized} because the remote 49 version won't be constructed with any arguments or keywords. 50 """ 51 def __init__(self): 52 pass 53 54 55 class Meta(type): 56 """ 57 """ 58 originalVersions = {} 59 validBases = [(__name__, "Parameterized")] 60 61 def __new__(metacls, name, bases, dictionary): 62 def realClass(cls): 63 return metacls.originalVersions.get(cls, cls) 64 65 def mcNames(cls): 66 cls = realClass(cls) 67 return cls.__module__, cls.__name__ 68 69 remoteable = False 70 if name.split("_")[0] not in ('Local', 'Remote'): 71 newBases = [] 72 for base in [realClass(x) for x in bases]: 73 if mcNames(base) in metacls.validBases: 74 remoteable = True 75 newBases.append(base) 76 bases = tuple(newBases) 77 cls = super(Meta, metacls).__new__(metacls, name, bases, dictionary) 78 if not remoteable: 79 return cls 80 mName, cName = mcNames(cls) 81 metacls.validBases.append((mName, cName)) 82 pb.globalSecurity.allowModules(mName) 83 pb.globalSecurity.allowInstancesOf(cls) 84 LocalClass = new.classobj( 85 "Local_%s" % name, (LocalVersion, cls), dictionary) 86 metacls.originalVersions[LocalClass] = cls 87 RemoteClass = new.classobj( 88 "Remote_%s" % name, (RemoteVersion, cls), {}) 89 pb.setUnjellyableForClass(LocalClass, RemoteClass) 90 return LocalClass 91 92 93 class Parameterized(object): 31 class Parameterized(Jellyable, Unjellyable, object): 94 32 """ 95 33 @cvar keyAttrs: A dict of attributes on which cache keying will be … … 102 40 103 41 """ 104 __metaclass__ = Meta105 106 42 paramNames, keyAttrs = [], {} 107 43 … … 110 46 Sets up my caching with a dict of parameters. 111 47 112 Keywords can be supplied (in some cases, must be supplied) that specify113 the names and values of attributes that are incorporated into the cache114 keying.48 When instantiated as an original (not remote) version, keywords can be 49 supplied (in some cases, must be supplied) that specify the names and 50 values of attributes that are incorporated into the cache keying. 115 51 """ 116 52 for name, default in self.keyAttrs.iteritems(): … … 164 100 def key(self, *args): 165 101 """ 102 Returns a key that is based on the hashes of my key attributes and any 103 arguments supplied. With some recursion as needed, hashes are done even 104 in cases where they couldn't be ordinarily. 166 105 """ 167 keys = self.keyAttrs.keys() 168 keys.sort() 169 result = [getattr(self, x) for x in keys] + list(args) 170 return tuple(result) 171 106 def superHash(x): 107 if not isinstance(x, (list, tuple, dict)): 108 return hash(x) 109 if isinstance(x, dict): 110 x = x.items() 111 return sum([superHash(y) for y in x]) 112 113 keys = self.keyAttrs.keys(); keys.sort() 114 return hash(args) + superHash([getattr(self, x) for x in keys]) 115 116 #--- Jelly/Unjelly API ---------------------------------------------------- 117 118 def getStateFor(self, jellier): 119 """ 120 Generates my state for jellying, which consists of the value of my key 121 attributes and parameters. Also registers my (sub)class and its module 122 as being safe for unjellying. 123 """ 124 state = {} 125 for sequence in (self.keyAttrs.keys(), self.paramNames): 126 for name in sequence: 127 if hasattr(self, name): 128 state[name] = getattr(self, name) 129 myClass = self.__class__ 130 pb.globalSecurity.allowInstancesOf(myClass) 131 pb.globalSecurity.allowModules(myClass.__module__) 132 return state 172 133 173 134 projects/Twisted-Goodies/trunk/twisted_goodies/pybywire/test/test_params.py
r98 r100 43 43 def anotherFunc(self, x): 44 44 return 10*x 45 45 46 class MetaThingy(params.Parameterized): 47 keyAttrs = {'myThingy': Thingy(a=10.0, c=1, d=2)} 48 paramNames = ('foo',) 49 50 def func(self, x): 51 return self.myThingy.func(x) 52 46 53 47 54 class Test_Parameterized_Caching(mock.TestCase): … … 80 87 81 88 def test_local_state(self): 82 state = self.ct.getState ToCopy()89 state = self.ct.getStateFor(None) 83 90 self.failUnlessElementsEqual(state.keys(), ('a', 'b', 'c', 'd')) 84 91 self.failUnlessElementsEqual(state.values(), (1.0, 2.0, 3.0, 4.0)) … … 90 97 self.copyable = copyable 91 98 def remote_takeMyCopy(self, thingy): 92 print "TMC", thingy93 99 self.copyable = thingy 94 100 def remote_giveMeCopy(self, null): … … 112 118 return self.server.stopListening() 113 119 120 def _checkCopy(self, result, ct, *attrNames): 121 self.failIfEqual(id(result), id(ct)) 122 self.failUnless(isinstance(result, params.Parameterized)) 123 for name in attrNames: 124 self.failUnlessEqual(getattr(result, name), getattr(ct, name)) 125 return result 126 127 def _oops(self, failure): 128 print "\nOOPS:\n%s" % failure.getTraceback() 129 114 130 def test_remoteVersion_Baseclass(self): 115 def got(result):116 self.failIfEqual(result, ct)117 self.failUnless(isinstance(result, params.RemoteVersion))118 self.failUnlessEqual(result.a, ct.a)119 120 131 ct = Thingy(a=1.0, b=2.0, c=3.0, d=4.0) 121 132 d = self.getReferenceToRoot(self.CopyableReturner(ct)) 122 133 d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 123 d.addCallback(got) 134 d.addErrback(self._oops) 135 d.addCallback(self._checkCopy, ct, 'a') 124 136 return d 125 137 126 138 def test_remoteVersion_Subclass(self): 127 def got(result):128 self.failIfEqual(result, ct)129 self.failUnless(isinstance(result, params.RemoteVersion))130 self.failUnlessEqual(result.a, ct.a)131 132 139 ct = SubThingy(a=1.0, b=2.0, c=3.0, d=4.0) 133 140 d = self.getReferenceToRoot(self.CopyableReturner(ct)) 134 141 d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 135 d.addCallback(got) 142 d.addErrback(self._oops) 143 d.addCallback(self._checkCopy, ct, 'a') 136 144 return d 137 145 138 146 def test_remoteVersion_BackAndForth(self): 139 def got(result):140 print "\nGOT", result141 print "vs", ct142 self.failIfEqual(result, ct)143 self.failUnless(isinstance(result, params.RemoteVersion))144 self.failUnlessEqual(result.a, ct.a)145 146 def oops(failure):147 print "\nOOPS:\n%s" % failure.getTraceback()148 149 147 ct = Thingy(a=1.0, b=2.0, c=3.0, d=4.0) 150 148 d = self.getReferenceToRoot(self.CopyableReturner(None)) 151 149 d.addCallback(lambda _: self.ref.callRemote("takeMyCopy", ct)) 152 d.addErrback(oops)153 150 d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 154 d.addErrback( oops)155 d.addCallback( got)151 d.addErrback(self._oops) 152 d.addCallback(self._checkCopy, ct, 'a') 156 153 return d 154 155 def test_remoteVersion_Nesting(self): 156 def checkSubThingy(rct): 157 self.failUnlessEqual(rct.func(10), ct.func(10)) 158 159 ct = MetaThingy(foo='bar') 160 d = self.getReferenceToRoot(self.CopyableReturner(ct)) 161 d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 162 d.addErrback(self._oops) 163 d.addCallback(self._checkCopy, ct, 'foo') 164 d.addCallback(checkSubThingy) 165 return d
