Handling async in Python - python

I'm coming from node where handling asynchronous design is as simple as adding a callback and getting on with your life. I'm trying to write some apps in python where I'm not having the same success and I'm struggling to find what to search for since there doesn't seem to be a direct equivalent.
Here's an example where I'm running an MQTT messaging client and waiting for a state change signal from a sensor.
import paho.mqtt.client as mqtt
from ouimeaux.environment import Environment
from ouimeaux.signals import receiver, statechange
def on_connect(client, userdata, rc):
print('Connected with result code '+str(rc))
client.subscribe('lights/#')
def turn_lights_on(client, userdata, rc):
for (x, value) in enumerate(devices['switches']):
devices['switches'][x].on()
def turn_lights_off(client, userdata, rc):
for (x, value) in enumerate(devices['switches']):
devices['switches'][x].off()
def reply_with_devices(client, userdata, rc):
for (x, value) in enumerate(devices['switches']):
client.publish('devices/new', switches[x])
for (x, value) in enumerate(devices['motions']):
client.publish('devices/new', motions[x])
def on_switch(switch):
print "Switch found: ", switch.name
devices['switches'].append(switch)
def on_motion(motion):
print "Motion found: ", motion.name
devices['motions'].append(motion)
client = mqtt.Client("wemo_controller")
client.on_connect = on_connect
client.message_callback_add('lights/on', turn_lights_on)
client.message_callback_add('lights/off', turn_lights_off)
client.message_callback_add('devices/discover', reply_with_devices)
client.connect('localhost', 1883, 60)
print 'Running WEMO controller - listening for messages on localhost:1883'
devices = { 'switches': [], 'motions': [] }
env = Environment(on_switch, on_motion)
env.start()
env.discover(seconds=3)
switch = env.get_switch('Desk lights')
#receiver(statechange)
def motion(sender, **kwargs):
print 'A THING HAPPENED'
print "{} state is {state}".format(sender.name, state="on" if kwargs.get('state') else "off")
env.wait()
client.loop_forever()
Both libraries seem to have their own way of holding up the thread but it seems like I can only have one blocking and listening at a time. I have a feeling that threading might be the answer, but I'm struggling to work out how to implement this and not sure if it's right. I'm also confused as to what wait() and loop_forever() actually do.
The answer I'm looking for is the 'python' way to solve this problem.

You may want to look at the Twisted framework
"Twisted is an event-driven networking engine written in Python"
It is specifically designed for building asynchronous network applications.
In particular, read up on the reactor, and using Deffered() to register callbacks

Asynchronous programming have been integrated into python recently. So,
if you are using python 3.3, then python provides an inbuilt library Asyncio especially for this purpose (which was previously called 'Tulips').
If you are using python 2.7, then you can use Trollius which is backporting of Asyncio. If nothing suits you, than you can obviously use full-fledged network programming framework Twisted as suggested in other answers.

I'm the author of HBMQTT, a MQTT broker/client library which uses Python asyncio API.
The client API doesn't need any callback. You can use the client API to subscribe for some topic and then run a loop for reading and processing incoming messages. Something like:
import asyncio
from hbmqtt.client import MQTTClient
C = MQTTClient()
#asyncio.coroutine
def test_coro():
yield from C.connect(uri='mqtt://localhost/', username=None, password=None)
# Adapt QOS as needed
yield from C.subscribe([
{'filter': 'lights/on', 'qos': 0x01},
{'filter': 'lights/off', 'qos': 0x01},
{'filter': 'devices/discover', 'qos': 0x01},
])
while some_condition:
# Wait until next PUBLISH message arrives
message = yield from C.deliver_message()
if message.variable_header.topic_name == 'lights/on':
# Lights on
elif message.variable_header.topic_name == 'lights/off':
# Lights off
yield from C.acknowledge_delivery(message.variable_header.packet_id)
yield from C.disconnect()
if __name__ == '__main__':
loop=asyncio.get_event_loop()
loop.run_until_complete(test_coro())
HBMQTT is still under development. It requires Python 3.4.

Related

Difference between websocket and websockets

I don't understand the difference between websocket (websocket client) and websockets.
I would like to understand the difference to know which one uses to be optimal.
I have a code that seems to do the same thing.
import websockets
import asyncio
import json
async def capture_data():
uri = "wss://www.bitmex.com/realtime?subscribe=instrument:XBTUSD"
#uri = "wss://www.bitmex.com/realtime"
async with websockets.connect(uri) as websocket:
while True:
data = await websocket.recv()
data = json.loads(data)
try :
#print('\n', data['data'][0]["lastPrice"])
print('\n', data)
except KeyError:
continue
asyncio.get_event_loop().run_until_complete(capture_data())
import websocket
import json
def on_open(ws):
print("opened")
auth_data = {
'op': 'subscribe',
'args': ['instrument:XBTUSD']
}
ws.send(json.dumps(auth_data))
def on_message(ws, message):
message = json.loads(message)
print(message)
def on_close(ws):
print("closed connection")
socket = "wss://www.bitmex.com/realtime"
ws = websocket.WebSocketApp(socket,
on_open=on_open,
on_message=on_message,
on_close=on_close)
ws.run_forever()
EDIT:
Is Python powerful enough to receive a lot of data via websocket?
In times of high traffic, I could have thousands of messages per second and even tens of thousands for short periods of time.
I've seen a lot of things about Nodejs that might be better than python but I don't know js and even if I just copy code from the internet I'm not sure I wouldn't waste time if I have to send the data to my python script and then process it.
websocket-client implements only client-side. If you want to create a WebSocket Server you have to use websockets.
websocket-client implements WebSocketApp which is more JavaScript like implementation of websocket.
I prefer WebSocketApp run as thread:
socket = "wss://www.bitmex.com/realtime"
ws = websocket.WebSocketApp(socket,
on_open=on_open,
on_message=on_message,
on_close=on_close)
wst = threading.Thread(target=lambda: ws.run_forever())
wst.daemon = True
wst.start()
# your code continues here ...
Now you have asynchronous WebSocket. Your mainframe program is running and every time, when you receive the message, function on_message is called. on_message process the message automatically, without mainframe interruption.
websockets is a Python standard libary, currently suggested to use.
As far as I understand the main difference, from the client API programming, is that websocket-client support callbacks programming paradigm, in a way similar of javascript web client APIs.
Read the websockets documentation FAQs:
Are there onopen, onmessage, onerror, and onclose callbacks?
No, there aren’t.
websockets provides high-level, coroutine-based APIs. Compared to callbacks, coroutines make it easier to manage control flow in concurrent code.
If you prefer callback-based APIs, you should use another library.
With current Python 3 release the common / suggested way to manage asyncronous coroutines is via asyncio (used by websockets standard library approach).
So, answering your last question (Python vs nodejs), asyncio just allows to manage asyncronous I/O (networking-based in this case) events, as NodeJs does natively. broadley speaking, using asyncio you can achieve I/O websockets performances similar to that obtained using Nodejs.

Websocket/event-source/... implementation to expose a two-way RPC to a python/django application

for a django application I'm working on, I need to implement a two ways RPC so :
the clients can call RPC methods from the platform and
the platform can call RPC methods from each client.
As the clients will mostly be behind NATs (which means no public IPs, and unpredictable weird firewalling policies), the platform to client way has to be initiated by the client.
I have a pretty good idea on how I can write this from scratch, I also think I can work something out of the publisher/subscriber model of twisted, but I've learned that there is always a best way to do it in python.
So I'm wondering what would be the best way to do it, that would also integrate the best to django. The code will have to be able to scope with hundreds of clients in short term, and (we hope) with thousands of clients in medium/long term.
So what library/implementation would you advice me to use ?
I'm mostly looking to starting points for RTFM !
websocket is a moving target, with new specifications from time to time. Brave developpers implements server side library, but few implements client side. The client for web socket is a web browser.
websocket is not the only way for a server to talk to a client, event source is a simple and pragmatic way to push information to a client. It's just a never ending page. Twitter fire hose use this tricks before its specification. The client open a http connection and waits for event. The connection is kept open, and reopen if there is some troubles (connection cut, something like that).
No timeout, you can send many events in one connection.
The difference between websocket and eventsource is simple. Websocket is bidirectionnal and hard to implement. Eventsource is unidirectionnal and simple to implement, both client and server side.
You can use eventsource as a zombie controller. Each client connects and reconnect to the master and wait for instruction. When instruction is received, the zombie acts and if needed can talk to its master, with a classical http connection, targeting the django app.
Eventsource keep the connection open, so you need an async server, like tornado. Django need a sync server, so, you need both, with a dispatcher, like nginx. Django or a cron like action talks to the async server, wich talks to the right zombie. Zombie talks to django, so, the async server doesn't need any peristance, it's just a hub with plugged zombies.
Gevent is able to handle such http server but there is no decent doc and examples for this point. It's a shame. I want a car, you give me a screw.
You can also use Tornado + Tornadio + Socket.io. That's what we are using right now for notifications, and the amount of code that you should write is not that much.
from tornadio2 import SocketConnection, TornadioRouter, SocketServer
class RouterConnection(SocketConnection):
__endpoints__ = {'/chat': ChatConnection,
'/ping': PingConnection,
'/notification' : NotificationConnection
}
def on_open(self, info):
print 'Router', repr(info)
MyRouter = TornadioRouter(RouterConnection)
# Create socket application
application = web.Application(
MyRouter.apply_routes([(r"/", IndexHandler),
(r"/socket.io.js", SocketIOHandler)]),
flash_policy_port = 843,
flash_policy_file = op.join(ROOT, 'flashpolicy.xml'),
socket_io_port = 3001,
template_path=os.path.join(os.path.dirname(__file__), "templates/notification")
)
class PingConnection(SocketConnection):
def on_open(self, info):
print 'Ping', repr(info)
def on_message(self, message):
now = dt.utcnow()
message['server'] = [now.hour, now.minute, now.second, now.microsecond / 1000]
self.send(message)
class ChatConnection(SocketConnection):
participants = set()
unique_id = 0
#classmethod
def get_username(cls):
cls.unique_id += 1
return 'User%d' % cls.unique_id
def on_open(self, info):
print 'Chat', repr(info)
# Give user unique ID
self.user_name = self.get_username()
self.participants.add(self)
def on_message(self, message):
pass
def on_close(self):
self.participants.remove(self)
def broadcast(self, msg):
for p in self.participants:
p.send(msg)
here is a really simple solution I could came up with :
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.set_header("Content-Type", "text/event-stream")
self.set_header("Cache-Control", "no-cache")
self.write("Hello, world")
self.flush()
for i in range(0, 5):
msg = "%d<br>" % i
self.write("%s\r\n" % msg) # content
self.flush()
time.sleep(5)
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
and
curl http://localhost:8888
gives output when it comes !
Now, I'll just have to implement the full event-source spec and some kind of data serialization between the server and the clients, but that's trivial. I'll post an URL to the lib I'll write here when it'll be done.
I've recently played with Django, Server-Sent Events and WebSocket, and I've wrote an article about it at http://curella.org/blog/2012/jul/17/django-push-using-server-sent-events-and-websocket/
Of course, this comes with the usual caveats that Django probably isn't the best fit for evented stuff, and both protocols are still drafts.

Stomp.py return message from listener

Using stomp.py (3.0.5) with python (2.6) alongside Apache ActiveMQ (5.5.1). I have got the basic example working without any problems, but now I want to return the received message (in on_message()) to a variable outside the MyListener class.
I can imagine this is a pretty standard task, but my general python skills aren't good enough to work out how to do it. I've trawled google for a more advanced example and read up on global variables, but I still can't seem to get the message into a variable rather than just printing it to screen.
Any help, hugely appreciated!
Since the listener will be called in receiver thread, you should do a thread handoff if you want to process the message in other thread (main thread, for example).
One simple example of thread handoff is using a shared variable with locking and update that variable when message is received by the receiver thread. And, read that variable in the other thread but you need to use proper synchronization mechanism to make sure that you don't override the message, and you will not run into deadlocks.
Here is the sample code to use some global variable with locking.
rcvd_msg = None
lock = thread.Condition()
# executed in the main thread
with lock:
while rcvd_msg == None:
lock.wait()
# read rcvd_msg
rcvd_msg = None
lock.notifyAll()
class Listener(ConnectionListener):
def on_message(self, headers, message):
# executed in the receiver thread
global rcvd_msg, lock
with lock:
while rcvd_msg != None:
lock.wait()
rcvd_msg = message
lock.notifyAll()
Hope that helps!!
All you have to do, is a slight change of the listener class:
class MyListener(object):
msg_list = []
def __init__(self):
self.msg_list = []
def on_error(self, headers, message):
self.msg_list.append('(ERROR) ' + message)
def on_message(self, headers, message):
self.msg_list.append(message)
And in the code, where u use stomp.py:
conn = stomp.Connection()
lst = MyListener()
conn.set_listener('', lst)
conn.start()
conn.connect()
conn.subscribe(destination='/queue/test', id=1, ack='auto')
time.sleep(2)
messages = lst.msg_list
conn.disconnect()
return render(request, 'template.html', {'messages': messages})
Stomp.py how to return message from listener - a link to stackoverflow similar question

How to implement a two way jsonrpc + twisted server/client

Hello I am working on develop a rpc server based on twisted to serve several microcontrollers which make rpc call to twisted jsonrpc server. But the application also required that server send information to each micro at any time, so the question is how could be a good practice to prevent that the response from a remote jsonrpc call from a micro be confused with a server jsonrpc request which is made for a user.
The consequence that I am having now is that micros are receiving bad information, because they dont know if netstring/json string that is comming from socket is their response from a previous requirement or is a new request from server.
Here is my code:
from twisted.internet import reactor
from txjsonrpc.netstring import jsonrpc
import weakref
creds = {'user1':'pass1','user2':'pass2','user3':'pass3'}
class arduinoRPC(jsonrpc.JSONRPC):
def connectionMade(self):
pass
def jsonrpc_identify(self,username,password,mac):
""" Each client must be authenticated just after to be connected calling this rpc """
if creds.has_key(username):
if creds[username] == password:
authenticated = True
else:
authenticated = False
else:
authenticated = False
if authenticated:
self.factory.clients.append(self)
self.factory.references[mac] = weakref.ref(self)
return {'results':'Authenticated as %s'%username,'error':None}
else:
self.transport.loseConnection()
def jsonrpc_sync_acq(self,data,f):
"""Save into django table data acquired from sensors and send ack to gateway"""
if not (self in self.factory.clients):
self.transport.loseConnection()
print f
return {'results':'synced %s records'%len(data),'error':'null'}
def connectionLost(self, reason):
""" mac address is searched and all reference to self.factory.clientes are erased """
for mac in self.factory.references.keys():
if self.factory.references[mac]() == self:
print 'Connection closed - Mac address: %s'%mac
del self.factory.references[mac]
self.factory.clients.remove(self)
class rpcfactory(jsonrpc.RPCFactory):
protocol = arduinoRPC
def __init__(self, maxLength=1024):
self.maxLength = maxLength
self.subHandlers = {}
self.clients = []
self.references = {}
""" Asynchronous remote calling to micros, simulating random calling from server """
import threading,time,random,netstring,json
class asyncGatewayCalls(threading.Thread):
def __init__(self,rpcfactory):
threading.Thread.__init__(self)
self.rpcfactory = rpcfactory
"""identifiers of each micro/client connected"""
self.remoteMacList = ['12:23:23:23:23:23:23','167:67:67:67:67:67:67','90:90:90:90:90:90:90']
def run(self):
while True:
time.sleep(10)
while True:
""" call to any of three potential micros connected """
mac = self.remoteMacList[random.randrange(0,len(self.remoteMacList))]
if self.rpcfactory.references.has_key(mac):
print 'Calling %s'%mac
proto = self.rpcfactory.references[mac]()
""" requesting echo from selected micro"""
dataToSend = netstring.encode(json.dumps({'method':'echo_from_micro','params':['plop']}))
proto.transport.write(dataToSend)
break
factory = rpcfactory(arduinoRPC)
"""start thread caller"""
r=asyncGatewayCalls(factory)
r.start()
reactor.listenTCP(7080, factory)
print "Micros remote RPC server started"
reactor.run()
You need to add a enough information to each message so that the recipient can determine how to interpret it. Your requirements sounds very similar to those of AMP, so you could either use AMP instead or use the same structure as AMP to identify your messages. Specifically:
In requests, put a particular key - for example, AMP uses "_ask" to identify requests. It also gives these a unique value, which further identifies that request for the lifetime of the connection.
In responses, put a different key - for example, AMP uses "_answer" for this. The value matches up with the value from the "_ask" key in the request the response is for.
Using an approach like this, you just have to look to see whether there is an "_ask" key or an "_answer" key to determine if you've received a new request or a response to a previous request.
On a separate topic, your asyncGatewayCalls class shouldn't be thread-based. There's no apparent reason for it to use threads, and by doing so it is also misusing Twisted APIs in a way which will lead to undefined behavior. Most Twisted APIs can only be used in the thread in which you called reactor.run. The only exception is reactor.callFromThread, which you can use to send a message to the reactor thread from any other thread. asyncGatewayCalls tries to write to a transport, though, which will lead to buffer corruption or arbitrary delays in the data being sent, or perhaps worse things. Instead, you can write asyncGatewayCalls like this:
from twisted.internet.task import LoopingCall
class asyncGatewayCalls(object):
def __init__(self, rpcfactory):
self.rpcfactory = rpcfactory
self.remoteMacList = [...]
def run():
self._call = LoopingCall(self._pokeMicro)
return self._call.start(10)
def _pokeMicro(self):
while True:
mac = self.remoteMacList[...]
if mac in self.rpcfactory.references:
proto = ...
dataToSend = ...
proto.transport.write(dataToSend)
break
factory = ...
r = asyncGatewayCalls(factory)
r.run()
reactor.listenTCP(7080, factory)
reactor.run()
This gives you a single-threaded solution which should have the same behavior as you intended for the original asyncGatewayCalls class. Instead of sleeping in a loop in a thread in order to schedule the calls, though, it uses the reactor's scheduling APIs (via the higher-level LoopingCall class, which schedules things to be called repeatedly) to make sure _pokeMicro gets called every ten seconds.

Writing a blocking wrapper around twisted's IRC client

I'm trying to write a dead-simple interface for an IRC library, like so:
import simpleirc
connection = simpleirc.Connect('irc.freenode.net', 6667)
channel = connection.join('foo')
find_command = re.compile(r'google ([a-z]+)').findall
for msg in channel:
for t in find_command(msg):
channel.say("http://google.com/search?q=%s" % t)
Working from their example, I'm running into trouble (code is a bit lengthy, so I pasted it here). Since the call to channel.__next__ needs to be returned when the callback <IRCClient instance>.privmsg is called, there doesn't seem to be a clean option. Using exceptions or threads seems like the wrong thing here, is there a simpler (blocking?) way of using twisted that would make this possible?
In general, if you're trying to use Twisted in a "blocking" way, you're going to run into a lot of difficulties, because that's neither the way it's intended to be used, nor the way in which most people use it.
Going with the flow is generally a lot easier, and in this case, that means embracing callbacks. The callback-style solution to your question would look something like this:
import re
from twisted.internet import reactor, protocol
from twisted.words.protocols import irc
find_command = re.compile(r'google ([a-z]+)').findall
class Googler(irc.IRCClient):
def privmsg(self, user, channel, message):
for text in find_command(message):
self.say(channel, "http://google.com/search?q=%s" % (text,))
def connect():
cc = protocol.ClientCreator(reactor, Googler)
return cc.connectTCP(host, port)
def run(proto):
proto.join(channel)
def main():
d = connect()
d.addCallback(run)
reactor.run()
This isn't absolutely required (but I strongly suggest you consider trying it). One alternative is inlineCallbacks:
import re
from twisted.internet import reactor, protocol, defer
from twisted.words.protocols import irc
find_command = re.compile(r'google ([a-z]+)').findall
class Googler(irc.IRCClient):
def privmsg(self, user, channel, message):
for text in find_command(message):
self.say(channel, "http://google.com/search?q=%s" % (text,))
#defer.inlineCallbacks
def run():
cc = protocol.ClientCreator(reactor, Googler)
proto = yield cc.connectTCP(host, port)
proto.join(channel)
def main():
run()
reactor.run()
Notice no more addCallbacks. It's been replaced by yield in a decorated generator function. This could get even closer to what you asked for if you had a version of Googler with a different API (the one above should work with IRCClient from Twisted as it is written - though I didn't test it). It would be entirely possible for Googler.join to return a Channel object of some sort, and for that Channel object to be iterable like this:
#defer.inlineCallbacks
def run():
cc = protocol.ClientCreator(reactor, Googler)
proto = yield cc.connectTCP(host, port)
channel = proto.join(channel)
for msg in channel:
msg = yield msg
for text in find_command(msg):
channel.say("http://google.com/search?q=%s" % (text,))
It's only a matter of implementing this API on top of the ones already present. Of course, the yield expressions are still there, and I don't know how much this will upset you. ;)
It's possible to go still further away from callbacks and make the context switches necessary for asynchronous operation to work completely invisible. This is bad for the same reason it would be bad for sidewalks outside your house to be littered with invisible bear traps. However, it's possible. Using something like corotwine, itself based on a third-party coroutine library for CPython, you can have the implementation of Channel do the context switching itself, rather than requiring the calling application code to do it. The result might look something like:
from corotwine import protocol
def run():
proto = Googler()
transport = protocol.gConnectTCP(host, port)
proto.makeConnection(transport)
channel = proto.join(channel)
for msg in channel:
for text in find_command(msg):
channel.say("http://google.com/search?q=%s" % (text,))
with an implementation of Channel that might look something like:
from corotwine import defer
class Channel(object):
def __init__(self, ircClient, name):
self.ircClient = ircClient
self.name = name
def __iter__(self):
while True:
d = self.ircClient.getNextMessage(self.name)
message = defer.blockOn(d)
yield message
This in turn depends on a new Googler method, getNextMessage, which is a straightforward feature addition based on existing IRCClient callbacks:
from twisted.internet import defer
class Googler(irc.IRCClient):
def connectionMade(self):
irc.IRCClient.connectionMade(self)
self._nextMessages = {}
def getNextMessage(self, channel):
if channel not in self._nextMessages:
self._nextMessages[channel] = defer.DeferredQueue()
return self._nextMessages[channel].get()
def privmsg(self, user, channel, message):
if channel not in self._nextMessages:
self._nextMessages[channel] = defer.DeferredQueue()
self._nextMessages[channel].put(message)
To run this, you create a new greenlet for the run function and switch to it, and then start the reactor.
from greenlet import greenlet
def main():
greenlet(run).switch()
reactor.run()
When run gets to its first asynchronous operation, it switches back to the reactor greenlet (which is the "main" greenlet in this case, but it doesn't really matter) to let the asynchronous operation complete. When it completes, corotwine turns the callback into a greenlet switch back into run. So run is granted the illusion of running straight through, like a "normal" synchronous program. Keep in mind that it is just an illusion, though.
So, it's possible to get as far away from the callback-oriented style that is most commonly used with Twisted as you want. It's not necessarily a good idea, though.

Categories