async-pep
async-pep copied to clipboard
Example of an Async API compatible protocol
Needs to use:
- two-way communication
- consumers and producers
- nothing outside the stdlib
This should then ideally work on the stdlib implementation of the Async API backend, of course.
I'm thinking about using JSONRPC as the reference protocol here. Motivation:
- It's fairly simple
- You can speak it over raw TCP
- It can be composed with a decent HTTP protocol implementation later
- It has two-way communication
- It's easy to do using the tools that are available in the standard library anyway (mostly the
json
module) - It's actually useful and not in the standard library already
- XMLRPC is in the standard library already
I like a line based (\r\n terminated) chat server better because you can quite easily demonstrate essential facets:
- communication between protocol objects.
- connection made and connection lost events are needed in chat server (unlike in jsonrpc)
- can be interfaced with from 'telnet' to localhost - no one has a jsonrpc client with which to exercise their server.
- is actually a generalisation of an echo server, which is the basic starting point of most socket examples.
Examples used in core python socket module is here: https://babbledrive.appspot.com/static/doc/python-2.7.1/library/socket.html#example
Okay. That's a good argument. I like the idea of having LineReceiver
in the stdlib because it's so useful.
NetstringReceiver
in the stdlib would be even better.
I'd vote for ChunkReceiver
which is already in the PEP (and covers LineReceiver
) plus Twisted's ProcessProtocol
. I am not 100% sure whether I've got the user story in #25 right, but it seems to me that ProcessProtocol
would cover it.
I actually prefer a generic FrameProtocol
over ChunkReceiver
(which is a special case of it). I more or less use it in a Twisted context as follows (I show both Twisted-style protocol interface methods and callbacks to ease comparison; personally, I prefer handing over callbacks to subclassing):
class FrameProtocol(Protocol):
def __init__(self, parser, onFrame, onConnectionLost):
self._parser = parser
self._onFrame = onFrame
self._onConnectionLost = onConnectionLost
def connectionLost(self, reason):
self._onConnectionLost(reason)
def dataReceived(self, data):
self._parser.add(data)
for frame in iter(self._parser.get, self._parser.sentinel):
self.frameReceived(frame)
def frameReceived(self, frame):
self._onFrame(frame)
def send(self, frame):
self.transport.write(frame.compile())