[Twisted-Python] deferring result to PB a callRemote method

Chris Laws clawsicus at gmail.com
Mon Jan 18 09:32:05 EST 2010

I am writing a tool for work that will run various software development aids
such as message capturing and diagnostic control of system processes.
My current design strategy is to implement these aids as plugins (not
twisted plugins) to a generic plugin runner rather than stand alone
The plugins are spawned as separate processes by the plugin-runner and
communicate using Perspective Broker during the plugin installation and
shutdown phases.

Some plugins may take a while to shutdown as they need to close
sockets, finalize files, move large files, etc.
I would like to instruct the plugin to shutdown and be told when it is
finally ready to be shut down.

To date all my callRemote methods have effectively returned immediately. For

def remote_get_name(self):
    return self.name

My question to the list was going to be: Is there a pattern/example I could
follow where I could call plugin.callRemote("shutdown") on a plugin and not
return a result to the plugin-runner until the plugin has completed all
it's, potentially, long running activities? However, while I was trying to
write a small code snippet that would demonstrate what I was wanting, I
think I got it working.
I think the simple answer to my question is to just return a deferred as the
result to the callRemote("shutdown") method and trigger it as normal.
Google is my friend but I could not find examples of this usage. Is there
any references to this usage in the twisted docs?

Below (and attached in case the formatting goes screwy) is a short example
which emulates a long running shutdown activity performed by the plugin
prior to shutdown.
It seems to delay the processing of the callRemote("shutdown") result until
the plugin has completed it's long running activity.
I have omitted the separate process stuff as it didn't seem relevant for
this snippet.

Is the following code snippet the standard/normal way to defer the return
result of a callRemote method call?
If this is the normal way, how does triggering the deferred on the plugin
(client) side also trigger the same/copy deferred returned to the
plugin-runner (server)?
Is this PB magic, somehow managing deferreds across the PB interface?


from twisted.internet import reactor, defer
from twisted.spread import pb
import datetime

class PluginClient(pb.Referenceable):
    """ Plugin client interface exposed to PluginServer's """

    def __init__(self, shutdownCallback):
        self.shutdownHandler = shutdownCallback

    def remote_shutdown(self):
        """ Instruct Plugin to shutdown """
        print "plugin instructed to shutdown"
        d = defer.Deferred()
        return d

class PluginServer(pb.Root):
    """ Plugin server interface exposed to PluginClient's """

    def remote_set_client_perspective(self, pluginRef):
        print "plugin-runner got client reference"
        reactor.callLater(1.0, self.shutdown_plugin, pluginRef)

    def shutdown_plugin(self, pluginRef):

        def pluginShutdownCompleted(result, startTime):
            endTime = datetime.datetime.now()
            print "Plugin shutdown took %s to complete." % (endTime -
            return result

        print "plugin-runner asking plugin to shutdown"
        d = pluginRef.callRemote("shutdown")
        d.addCallback(pluginShutdownCompleted, datetime.datetime.now())

    def shutdown(self, _):

def startPluginServer(port):
    """ Start a plugin communications server """
    print "starting server"

def startPluginClient(port, shutdownHandler):
    """ Start a plugin communications client """

    def gotServerPerspective(serverPerspective, pluginPerspective):
        """ Give the plugin-runner this client's perspective """
        return serverPerspective

    print "starting plugin"
    client = PluginClient(shutdownHandler)
    factory = pb.PBClientFactory()
    reactor.connectTCP(host='localhost', port=port, factory=factory)
    return factory.getRootObject().addCallback(gotServerPerspective, client)

if __name__ == "__main__":
    port = 42155

    def longRunningAction(d, countDown=10):
        """ Emulate long running shutdown activities """
        print "shuting down in %i seconds" % countDown
        if countDown == 0:
            countDown -= 1
            reactor.callLater(1, longRunningAction, d, countDown)

    # start plugin-runner
    reactor.callWhenRunning(startPluginServer, port)
    # start plugin
    reactor.callLater(2.0, startPluginClient, port, longRunningAction)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://twistedmatrix.com/pipermail/twisted-python/attachments/20100119/a35c15fe/attachment-0001.htm 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_shutdown.py
Type: application/octet-stream
Size: 2856 bytes
Desc: not available
Url : http://twistedmatrix.com/pipermail/twisted-python/attachments/20100119/a35c15fe/attachment-0001.obj 

More information about the Twisted-Python mailing list