You can use loop.add_reader
schedule a callback to run when data is available on sys.stdin
, and then use an asyncio.Queue
to pass the stdin data received to your data_received
method:
import sys
import asyncio
def got_stdin_data(q):
asyncio.ensure_future(q.put(sys.stdin.readline()))
class EchoServerClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
message = data.decode()
print('Data received: {!r}'.format(message))
fut = asyncio.ensure_future(q.get())
fut.add_done_callback(self.write_reply)
def write_reply(self, fut):
reply = fut.result()
print('Send: {!r}'.format(reply))
self.transport.write(reply.encode())
#print('Close the client socket')
#self.transport.close()
q = asyncio.Queue()
loop = asyncio.get_event_loop()
loop.add_reader(sys.stdin, got_stdin_data, q)
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
# Serve requests until CTRL+c is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
The only tricky bit is how we call the Queue.put
/Queue.get
methods; both of them are coroutines, which can’t be called using yield from
in the callback or the Protocol
instance methods. Instead, we just schedule them with the event loop using asyncio.ensure_future
, and then use the add_done_callback
method to handle the reply we retrieve from the get()
call.
Note: asyncio.ensure_future
was introduced in Python 3.4.4. Prior to that, the method was called asyncio.async
. Additionally, Python 3.7 introduced asyncio.create_task
, which is now the preferred method.