I am trying to chain deferreds in AMP client like following:
Client:
from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol
from twisted.protocols.amp import AMP
import commands
def connect_protocol(host, port):
destination = TCP4ClientEndpoint(reactor, host, port)
d = connectProtocol(destination, AMP())
def connect(protocol):
print 'Connecting to server as Mr Spaceman...'
return protocol.callRemote(commands.Connect,
username='Mr Foo')
def say(protocol):
print 'Saying "Hello world" to the server...'
return protocol.callRemote(commands.Say,
phrase='Hello world')
d.addCallback(connect)
d.addCallback(say)
def main(host, port):
connect_protocol(host, port)
print 'Connected to %s:%d...' % (host, port)
reactor.run()
main('127.0.0.1', 12345)
Server:
from twisted.internet.protocol import Factory
from twisted.protocols.amp import AMP
import commands
class CommandProtocol(AMP):
def connect(self, username):
print "Received connect command: %s." % (username)
return {}
commands.Connect.responder(connect)
def say(self, phrase):
print "Received phrase \"%s\"." % phrase
return {}
commands.Say.responder(say)
def main(port):
factory = Factory()
factory.protocol = CommandProtocol
reactor.listenTCP(port, factory)
print 'Started AMP server on port %d...' % port
reactor.run()
main(12345)
Only connect()
is being fired on server side
First, enable logging:
from sys import stdout
from twisted.python.log import startLogging
startLogging(stdout)
Now you'll see what's going on in the program.
Second, at least have a final errback that logs unhandled failures on a Deferred
so these will show up deterministically rather than depending on the garbage collector:
from twisted.python.log import err
...
d.addCallback(connect)
d.addCallback(say)
d.addErrback(err, "connect_protocol encountered some problem")
Finally, the result of a Deferred is changed by callbacks and errbacks attached to it. In this case, the argument passed to say
is the result of the Deferred
returned by connect
. This will not be the same as the argument to connect
, so it's not likely you can use callRemote
on it.
You can fix this in many different ways. One way that involves minimal code changes (but isn't necessarily the best solution) is to pass the protocol as an extra value in the result of the connect
Deferred
:
def connect(protocol):
print 'Connecting to server as Mr Spaceman...'
d = protocol.callRemote(commands.Connect, username='Mr Foo')
d.addCallback(lambda result: (protocol, result))
return d
def say((protocol, result)):
print 'Saying "Hello world" to the server...'
return protocol.callRemote(commands.Say,
phrase='Hello world')