Changeset 100

Show
Ignore:
Timestamp:
11/13/07 17:11:03 (1 year ago)
Author:
edsuom
Message:

Simple and effective Parameterizable objects for passing around one or more times via PB

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • projects/Twisted-Goodies/trunk/twisted_goodies/pybywire/params.py

    r97 r100  
    2525""" 
    2626 
    27 import new 
    2827from twisted.spread import pb 
     28from twisted.spread.jelly import Jellyable, Unjellyable 
    2929 
    3030 
    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): 
     31class Parameterized(Jellyable, Unjellyable, object): 
    9432    """ 
    9533    @cvar keyAttrs: A dict of attributes on which cache keying will be 
     
    10240 
    10341    """ 
    104     __metaclass__ = Meta 
    105  
    10642    paramNames, keyAttrs = [], {} 
    10743 
     
    11046        Sets up my caching with a dict of parameters. 
    11147 
    112         Keywords can be supplied (in some cases, must be supplied) that specify 
    113         the names and values of attributes that are incorporated into the cache 
    114         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. 
    11551        """ 
    11652        for name, default in self.keyAttrs.iteritems(): 
     
    164100    def key(self, *args): 
    165101        """ 
     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. 
    166105        """ 
    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 
    172133 
    173134 
  • projects/Twisted-Goodies/trunk/twisted_goodies/pybywire/test/test_params.py

    r98 r100  
    4343    def anotherFunc(self, x): 
    4444        return 10*x 
    45              
     45 
     46class 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 
    4653 
    4754class Test_Parameterized_Caching(mock.TestCase): 
     
    8087 
    8188    def test_local_state(self): 
    82         state = self.ct.getStateToCopy(
     89        state = self.ct.getStateFor(None
    8390        self.failUnlessElementsEqual(state.keys(), ('a', 'b', 'c', 'd')) 
    8491        self.failUnlessElementsEqual(state.values(), (1.0, 2.0, 3.0, 4.0)) 
     
    9097            self.copyable = copyable 
    9198        def remote_takeMyCopy(self, thingy): 
    92             print "TMC", thingy 
    9399            self.copyable = thingy 
    94100        def remote_giveMeCopy(self, null): 
     
    112118            return self.server.stopListening() 
    113119 
     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 
    114130    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  
    120131        ct = Thingy(a=1.0, b=2.0, c=3.0, d=4.0) 
    121132        d = self.getReferenceToRoot(self.CopyableReturner(ct)) 
    122133        d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 
    123         d.addCallback(got) 
     134        d.addErrback(self._oops) 
     135        d.addCallback(self._checkCopy, ct, 'a') 
    124136        return d 
    125137 
    126138    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  
    132139        ct = SubThingy(a=1.0, b=2.0, c=3.0, d=4.0) 
    133140        d = self.getReferenceToRoot(self.CopyableReturner(ct)) 
    134141        d.addCallback(lambda _: self.ref.callRemote("giveMeCopy", ct)) 
    135         d.addCallback(got) 
     142        d.addErrback(self._oops) 
     143        d.addCallback(self._checkCopy, ct, 'a') 
    136144        return d 
    137145 
    138146    def test_remoteVersion_BackAndForth(self): 
    139         def got(result): 
    140             print "\nGOT", result 
    141             print "vs", ct 
    142             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  
    149147        ct = Thingy(a=1.0, b=2.0, c=3.0, d=4.0) 
    150148        d = self.getReferenceToRoot(self.CopyableReturner(None)) 
    151149        d.addCallback(lambda _: self.ref.callRemote("takeMyCopy", ct)) 
    152         d.addErrback(oops) 
    153150        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'
    156153        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