twisted client call from a library function

278 views Asked by At

I'm trying to implement a function that will act as a Twisted client. It's called from code I don't have control over. I tried something like (this taken from the pbsimpleclient.py example code):

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.


from twisted.spread import pb
from twisted.internet import reactor
from twisted.python import util

def remcall(**kw):
    factory = pb.PBClientFactory()
    reactor.connectTCP("localhost", 8789, factory)
    d = factory.getRootObject()
    # kw here is what's passed in via remcall
    d.addCallback(lambda object: object.callRemote("echo", kw))
    d.addCallback(lambda echo: 'server echoed: '+repr(echo))
    d.addErrback(lambda reason: 'error: '+str(reason.value))
    d.addCallback(util.println)
    d.addCallback(lambda _: reactor.stop())
    reactor.run()

And the caller would make a call like:

remcall(hello=1, world=2)
remcall(hi=3, there=4)

But as you may have guessed, it gives a "twisted.internet.error.ReactorNotRestartable" error.

What's the best way to do this? I'm not so worried about getting a response from the remote end, but I should know if it's failing and why.

2

There are 2 answers

0
mlv On BEST ANSWER

The answer is to use crochet.

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

## Add these two lines
from crochet import setup, wait_for
setup()

from twisted.spread import pb
from twisted.internet import reactor
from twisted.python import util

## Add a wait_for decorator
@wait_for(timeout=5.0)
def remcall(**kw):
    factory = pb.PBClientFactory()
    reactor.connectTCP("localhost", 8789, factory)
    d = factory.getRootObject()
    # kw here is what's passed in via remcall
    d.addCallback(lambda object: object.callRemote("echo", kw))
    d.addCallback(lambda echo: 'server echoed: '+repr(echo))
    d.addErrback(lambda reason: 'error: '+str(reason.value))
    d.addCallback(util.println)
## Get rid of the reactor calls, and return d
#    d.addCallback(lambda _: reactor.stop())
#    reactor.run()
    return d

Then the caller just calls

remcall(hello=1, world=2)
remcall(hi=3, there=4)

and crochet's @wait_for handles running remcall inside the reactor thread.

4
notorious.no On

Remove reactor.run() from the remcall function and append it at the end. Also remove d.addCallback(lambda _: reactor.stop())

def remcall(**kw):
    factory = pb.PBClientFactory()
    reactor.connectTCP("localhost", 8789, factory)
    d = factory.getRootObject()
    # kw here is what's passed in via remcall
    d.addCallback(lambda object: object.callRemote("echo", kw))
    d.addCallback(lambda echo: 'server echoed: '+repr(echo))
    d.addErrback(lambda reason: 'error: '+str(reason.value))
    d.addCallback(util.println)


remcall(hello=1, world=2)
remcall(hi=3, there=3)
reactor.run()    # this should be the last thing to run

The reactor can only be run once. reactor.stop() function is being executed and it should not unless your application needs to stop running completely. This is why you get the ReactorNotRestartable exception.