Struggling to understand Twisted in general and pb in particular
Question: Could someone explain the difference between the following please. I am really
struggling to grasp Deferred concept, I thought I had it as I have been doing
examples all day. But I think i must be code blind. I'm sure its really
simple.

This works.

    
    
    from twisted.spread import pb
    from twisted.internet import reactor
    from twisted.python import util
    
    
    if __name__ == '__main__':
    
        def print_result(result):
            print result
    
        def add_numbers(obj, a, b):
            obj.callRemote("add_numbers", a, b)
    
    
    
        factory = pb.PBClientFactory()
        reactor.connectTCP("localhost", 8789, factory)
        d = factory.getRootObject()
        d.addCallback(lambda object: object.callRemote("add_numbers", 1, 2))
        d.addErrback(lambda reason: 'error: '+str(reason.value))
        d.addCallback(print_result)
        d.addErrback(lambda reason: 'error: '+str(reason.value))
        d.addCallback(lambda _: reactor.stop())
        d = factory.getRootObject()
    
        reactor.run()
    

and this doesnt

    
    
    from twisted.spread import pb
    from twisted.internet import reactor
    from twisted.python import util
    
    
    if __name__ == '__main__':
    
        def print_result(result):
            print result
    
        def add_numbers(obj, a, b):
            obj.callRemote("add_numbers", a, b)
    
    
    
        factory = pb.PBClientFactory()
        reactor.connectTCP("localhost", 8789, factory)
        d = factory.getRootObject()
        d.addCallback(add_numbers, 1, 2)
        d.addErrback(lambda reason: 'error: '+str(reason.value))
        d.addCallback(print_result)
        d.addErrback(lambda reason: 'error: '+str(reason.value))
        d.addCallback(lambda _: reactor.stop())
        d = factory.getRootObject()
    
        reactor.run()
    

I cant for the life of me work out why, it crashes with this error:

    
    
    Unhandled error in Deferred:
    Unhandled Error
    Traceback (most recent call last):
    Failure: twisted.spread.pb.PBConnectionLost: [Failure instance: Traceback (failure  with no frames): <class 'twisted.internet.error.ConnectionLost'>: Connection to the other side was lost in a non-clean fashion: Connection lost.
    ]
    

Server side code is

    
    
    from twisted.spread import pb
    from twisted.internet import reactor
    
    class Echoer(pb.Root):
        def remote_echo(self, st):
            print 'echoing:', st
            return st
    
        def remote_add_numbers(self, a, b):
            print 'adding:', a, b
            c = a + b
            return c
    
    if __name__ == '__main__':
        reactor.listenTCP(8789, pb.PBServerFactory(Echoer()))
        reactor.run()
    


Answer: The difference between your working and broken examples is that the `lambda`
expression implicitly returns its result. Its result is a `Deferred`, which
means that the next callback in the chain will wait to execute until its
result is available.

If you change the definition of `add_numbers` to return the result of
`callRemote`, like so:

    
    
    def add_numbers(obj, a, b):
        return obj.callRemote("add_numbers", a, b)
    

Then your broken example will start working again.

