Python - How to write a qimessaging service
This guide will show you how to write a qimessaging service in Python, and advertise it to others.
- An installed python NAOqi SDK for your operating system.
Creating a service ¶
We are going to create simple service that raises a signal when its method bang is called.
To do that, let’s declare a simple class:
import qi class MyFooService: def __init__(self, *args, **kwargs): #define a signal 'onBang' self.onBang = qi.Signal() #define a bang method that will trigger the onBang signal def bang(self): #trigger the signal with 42 as value self.onBang(42)
Creating a session ¶
To make the service available to others we need a
We will use the session of the
that is already connected to a ServiceDirectory,
and register our MyFooService created just above under the name ‘foo’.
import qi import sys #create an application app = qi.Application() app.start() #create an instance of MyFooService myfoo = MyFooService() s = app.session #let's register our service with the name "foo" id = s.registerService("foo", myfoo) #let the application run app.run()
Test the service ¶
We are going to create a simple client to call the service and react to it.
import qi import sys def onBangCb(i): print "bang:", i app = qi.Application() app.start() s = app.session foo = s.service("foo") #register a callback on 'onBang' foo.onBang.connect(onBangCb) #call bang foo.bang()
Wrapping Asynchronous API ¶
If the API you are wrapping is asynchronous, you want to avoid blocking in your function just waiting for your asynchronous call to finish.
The procedure is the following:
You create a
You create a handler that will set the value of the
qi.Promisewhen the asynchronous call finish
- You launch your asynchronous call giving it the handler, that will be called on completion
You return the
qi.Futureto your user
For the example we will implement a mycrazydelay and mycrazydelay_async.
# stupid implementation of a function that: # - take time # - call finish_callback # - return the delay (or the result of the computation) # if this function was strictly used synchronously the callback you be useless def mycrazydelay(delay, finish_callback): time.sleep(delay) if finish_callback: finish_callback(delay) return delay # simple wrapper around mycrazydelay to make it asynchronous def mycrazydelay_async(delay, finish_callback): qi.async(mycrazydelay, args=(delay, finish_callback))
and create a Foo service that call this two methods:
- bar which wrap the synchronous mycrazydelay
- betterBar which wrap the asynchronous mycrazydelay_async
class Foo: #block a thread for 50sec :( def bar(self): return mycrazydelay(50, None) def betterBar(self): p = qi.Promise() # callback that will set the value on the promise def finish(d): p.setValue(d) # return immediately mycrazydelay_async(50, finish) # return the future that will have the value set when p.setValue is called return p.future()
The two functions bar and betterBar behave exactly the same from a client side of view. The first one will block a thread for 50sec, the second one is asynchronous, and keeps precious thread resources free. betterBar returns a Future, that will be notified when p.setValue is called.
returning a Future does not change what the client will see. If you return a Future containing a bool, the client will just see a bool. Future are not sent thought the network.
So if you want to use the API asynchronously you have to use qi.async.
Let’s look at a client example:
app = qi.Application() app.start() foo = app.session.service("foo") #both call are identical, they return a delay delay = foo.bar() delay = foo.betterBar() #to use them asynchronously: fut = qi.async(foo.bar) fut = qi.async(foo.betterBar) delay = fut.value()
Single-threaded / Multi-threaded mode for services ¶
By default, python services are in single-threaded mode. This means their methods cannot be called in parallel. You might encounter this kind of message if you call several methods of such a module at the same time.
[W] 1394221129.982971 4079 qitype.dynamicobject: Time-out acquiring object lock when calling method. Deadlock? [E] 1394221129.988775 4052 python: RuntimeError: Time-out acquiring lock. Deadlock? [W] 1394221137.850762 3859 qitype.dynamicobject: Time-out acquiring object lock when calling method. Deadlock?
If you need to call methods of your module in parallel, you will need to protect them yourself, and specify that your module is in multithread mode by using the qi.multiThreaded decorator.
@qi.multiThreaded() class Foo: # # Define your service #