RPCUDP : RPC over UDP in Python

RPC over UDP may seem like a silly idea, but things like the DHT Kademlia require it. This project is specifically designed for asynchronous Python 3 code to accept and send remote proceedure calls.
Because of the use of UDP, you will not always know whether or not a procedure call was successfully received. This isn't considered an exception state in the library, though you will know if a response isn't received by the server in a configurable amount of time.
Installation
pip install rpcudp
Usage
This assumes you have a working familiarity with asyncio.
First, let's make a server that accepts a remote procedure call and spin it up.
import asyncio
from rpcudp.protocol import RPCProtocol
class RPCServer(RPCProtocol):
def rpc_sayhi(self, sender, name):
return "Hello %s you live at %s:%i" % (name, sender[0], sender[1])
async def rpc_sayhi_slowly(self, sender, name):
await some_awaitable()
return "Hello %s you live at %s:%i" % (name, sender[0], sender[1])
async def main():
loop = asyncio.get_event_loop()
transport, protocol = await loop.create_datagram_endpoint(
RPCServer, local_addr=("127.0.0.1", 1234)
)
await asyncio.Event().wait()
asyncio.run(main())
Now, let's make a client script. Note that we do need to specify a port for the client as well, since it needs to listen for responses to RPC calls on a UDP port.
import asyncio
from rpcudp.protocol import RPCProtocol
async def sayhi(protocol, address):
result = await protocol.sayhi(address, "Snake Plissken")
print(result[1] if result[0] else "No response received.")
async def main():
loop = asyncio.get_event_loop()
transport, protocol = await loop.create_datagram_endpoint(
RPCProtocol, local_addr=("127.0.0.1", 4567)
)
await sayhi(protocol, ("127.0.0.1", 1234))
transport.close()
asyncio.run(main())
You can run this example in the examples folder (client.py and server.py).
Logging
This library uses the standard Python logging library. To see debut output printed to STDOUT, for instance, use:
import logging
log = logging.getLogger('rpcudp')
log.setLevel(logging.DEBUG)
log.addHandler(logging.StreamHandler())
Implementation Details
The protocol is designed to be as small and fast as possible. Python objects are serialized using MsgPack. All calls must fit within 8K (generally small enough to fit in one datagram packet).