So, I woke up this morning thinking about ZeroMQ and the problem of how it might be supported in Curio. Looking around in the source code for pyzmq, I noticed that they defined their own custom selector class for use with asyncio (https://github.com/zeromq/pyzmq/blob/master/zmq/asyncio/init.py).
Even though Curio doesn’t use any part of asyncio, it does use the same underlying selector classes for I/O polling. So, I got to wondering if I could just plug this selector into Curio and have it work. It turns out that the answer is yes. So, with that selector, a small Curio compatible version of the Socket class, and a custom zmq context, it seems to work. See the code: https://github.com/dabeaz/curio/blob/master/curio/zmq.py
Using that, you can write some fairly normal looking zmq code using Curio. For example, here is some code that pushes messages out on a ZMQ PUSH socket:
import curio.zmq as zmq async def pusher(address): ctx = zmq.Context() sock = ctx.socket(zmq.PUSH) sock.bind(address) for n in range(100): await sock.send(b'Message %d' % n) await sock.send(b'exit') if __name__ == '__main__': zmq.run(pusher('tcp://*:9000'))
Here is the corresponding client code:
import curio.zmq as zmq async def puller(address): ctx = zmq.Context() sock = ctx.socket(zmq.PULL) sock.connect(address) while True: msg = await sock.recv() if msg == b'exit': break print('Got:', msg) if __name__ == '__main__': zmq.run(puller('tcp://localhost:9000'))
For a day of coding, I’m probably missing something critical, but for now it all seems to work. Cool.