<div>I am writing a tool for work that will run various software development aids such as message capturing and diagnostic control of system processes.<div>My current design strategy is to implement these aids as plugins (not twisted plugins) to a generic plugin runner rather than stand alone applications.</div>
<div>The plugins are spawned as separate processes by the plugin-runner and communicate using Perspective Broker during the plugin installation and shutdown phases.</div><div><br></div><div>Some plugins may take a while to shutdown as they need to close sockets, finalize files, move large files, etc.</div>
<div>I would like to instruct the plugin to shutdown and be told when it is finally ready to be shut down.</div><div><br></div><div>To date all my callRemote methods have effectively returned immediately. For example:</div>
<div><br></div><div>def remote_get_name(self):</div><div> return <a href="http://self.name" target="_blank">self.name</a></div><div><br></div><div>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.</div>
<div>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.</div><div>Google is my friend but I could not find examples of this usage. Is there any references to this usage in the twisted docs?</div>
<div><br></div><div>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.</div><div>It seems to delay the processing of the callRemote("shutdown") result until the plugin has completed it's long running activity.</div>
<div>I have omitted the separate process stuff as it didn't seem relevant for this snippet.</div><div><br></div><div><div>Is the following code snippet the standard/normal way to defer the return result of a callRemote method call?</div>
<div>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)?</div><div>Is this PB magic, somehow managing deferreds across the PB interface?</div>
<div><br></div><div>Regards,</div><div>Chris</div><div><br></div><div><br></div></div><div><br></div><div><div><div>from twisted.internet import reactor, defer</div><div>from twisted.spread import pb</div><div>import datetime</div>
<div><br></div><div>class PluginClient(pb.Referenceable):</div><div> """ Plugin client interface exposed to PluginServer's """</div><div> </div><div> def __init__(self, shutdownCallback):</div>
<div> self.shutdownHandler = shutdownCallback</div><div><br></div><div> def remote_shutdown(self):</div><div> """ Instruct Plugin to shutdown """</div><div> print "plugin instructed to shutdown"</div>
<div> d = defer.Deferred()</div><div> self.shutdownHandler(d)</div><div> return d</div><div> </div><div>class PluginServer(pb.Root):</div><div> """ Plugin server interface exposed to PluginClient's """</div>
<div><br></div><div> def remote_set_client_perspective(self, pluginRef):</div><div> print "plugin-runner got client reference"</div><div> reactor.callLater(1.0, self.shutdown_plugin, pluginRef)</div>
<div> </div><div> def shutdown_plugin(self, pluginRef):</div><div> </div><div> def pluginShutdownCompleted(result, startTime):</div><div> endTime = datetime.datetime.now()</div><div> print "Plugin shutdown took %s to complete." % (endTime - startTime)</div>
<div> return result</div><div> </div><div> print "plugin-runner asking plugin to shutdown"</div><div> d = pluginRef.callRemote("shutdown")</div><div> d.addCallback(pluginShutdownCompleted, datetime.datetime.now())</div>
<div> d.addCallback(self.shutdown)</div><div> </div><div> def shutdown(self, _):</div><div> reactor.stop()</div><div><br></div><div><br></div><div>def startPluginServer(port):</div><div> """ Start a plugin communications server """</div>
<div> print "starting server"</div><div> reactor.listenTCP(port=port,</div><div> factory=pb.PBServerFactory(PluginServer()),</div><div> interface='localhost')</div>
<div><br></div><div><br></div><div>def startPluginClient(port, shutdownHandler):</div><div> """ Start a plugin communications client """</div><div> </div><div> def gotServerPerspective(serverPerspective, pluginPerspective):</div>
<div> """ Give the plugin-runner this client's perspective """</div><div> serverPerspective.callRemote("set_client_perspective", pluginPerspective)</div><div> return serverPerspective</div>
<div><br></div><div> print "starting plugin"</div><div> client = PluginClient(shutdownHandler)</div><div> factory = pb.PBClientFactory()</div><div> reactor.connectTCP(host='localhost', port=port, factory=factory)</div>
<div> return factory.getRootObject().addCallback(gotServerPerspective, client)</div><div><br></div><div>if __name__ == "__main__":</div><div> port = 42155</div><div> </div><div> def longRunningAction(d, countDown=10):</div>
<div> """ Emulate long running shutdown activities """</div><div> print "shuting down in %i seconds" % countDown</div><div> if countDown == 0:</div><div> d.callback(True)</div>
<div> else:</div><div> countDown -= 1</div><div> reactor.callLater(1, longRunningAction, d, countDown)</div><div><br></div><div> </div><div> # start plugin-runner</div><div> reactor.callWhenRunning(startPluginServer, port)</div>
<div> # start plugin</div><div> reactor.callLater(2.0, startPluginClient, port, longRunningAction)</div><div> reactor.run()</div></div></div><div><br></div><div><br></div></div>