[Twisted-Python] Synchronization techniques

Daniel Miller daniel at keystonewood.com
Wed Apr 4 10:35:10 MDT 2007


On Apr 3, 2007, at 8:42 PM, Itamar Shtull-Trauring wrote:

> On Tue, 2007-04-03 at 17:07 -0400, Daniel Miller wrote:
>
>>> twisted.internet.defer.DeferredLock and some of the related classes
>>> are
>>> what you ought to be using.
>>
>> Unfortunately that only gets me half way there. DeferredLock.acquire
>> () returns a deferred. How do I return the result of a deferred from
>> a PB remote_xxx() function?
>
> Just return the Deferred from the remote_xxx() function.
>

Thanks, I didn't know I could return a deferred from a PB remote_xxx 
() method. That detail doesn't seem to be documented in the  
Perspective Broker documentation, which I have read quite a few  
times. Maybe this could be highlighted in the "Complete Example" [0]  
section of the PB usage documentation? The examples use the  
TwistedQuotes application, and the IQuoter.getQuote() method always  
returns a string (at least I couldn't find any implementations that  
return a deferred). However, that would require rewriting most if not  
all implementations of IQuoter to return deferred's and/or the code  
that calls IQuoter.getQuote(), which demonstrates the viral nature of  
twisted when used in conjunction with other libraries.

So anyway, I rewrote my server-side library to do it the twisted way  
and return deferred's instead trying rig up some way of waiting for  
them. I still think it would be super useful to be able to pseudo- 
block on a deferred (i.e. allow the reactor to process other events  
while waiting for the deferred). It is very annoying to have to  
rewrite many layers of code when twisted is introduced into a  
program. I did find gthreadless.py, and maybe that would do it.  
Unfortunately discussion on that seems to have been dropped some time  
ago...

For the record, I've included updated versions of the previously  
posted code below. I'd be happy if someone pointed out if I'm doing  
anything wrong (with respect to twisted) in this code.

Thanks,
Daniel

[0] <http://twistedmatrix.com/projects/core/documentation/howto/pb- 
usage.html#auto1>

~~~~~~~~~

from twisted.internet.defer import Deferred, DeferredLock
from twisted.internet.error import ProcessTerminated
from twisted.internet.protocol import ProcessProtocol
from twisted.python.failure import Failure

class LockProxy(object):
     """A proxy that synchronizes method calls on a given subject"""

     def __init__(self, subject):
         self.lock = DeferredLock()
         self.subject = subject

     def __getattr__(self, name):
         value = getattr(self.subject, name)
         if hasattr(value, "im_self") and value.im_self is self.subject:
             def func(*args, **kw):
                 def callback(lock, *args, **kw):
                     try:
                         result = value(*args, **kw)
                     except:
                         lock.release()
                         raise
                     if isinstance(result, Deferred):
                         def release(arg, lock):
                             lock.release()
                             return arg
                         result.addBoth(release, lock)
                     else:
                         lock.release()
                     return result
                 dfr = self.lock.acquire()
                 dfr.addCallback(callback, *args, **kw)
                 return dfr
             return func
         return value

class ProcessError(Exception): pass

class ProcessErrorHandler(ProcessProtocol):

     def __init__(self, deferred):
         self.deferred = deferred
         self.errbuf = []
         self.errors = []

     def errReceived(self, text):
         self.errbuf.append(text)

     def processEnded(self, status):
         if self.errbuf:
             self.errors.append("".join(self.errbuf))
         if isinstance(status.value, ProcessTerminated):
             self.errors.append(status.getErrorMessage())
         if self.errors:
             err = Failure(ProcessError("\n".join(self.errors)))
             self.deferred.errback(err)
         else:
             self.deferred.callback(self)
         self.deferred = None

class TwistedProcess(object):

     def __init__(self, reactor=None, protocol=ProcessErrorHandler):
         if reactor is None:
             from twisted.internet import reactor
         self.reactor = reactor
         self.protocol = protocol

     def check_call(self, cmd):
         dfr = Deferred()
         protocol = self.protocol(dfr)
         self.reactor.spawnProcess(protocol, cmd[0], cmd,  
env=os.environ)
         return dfr






More information about the Twisted-Python mailing list