I've developed a pattern to use for a commanding a python daemon through cmd
module shell using an eventloop
. However, its not ready yet because I can't figure out how to gracefully exit the two applications (I'm still learning asyncio
and can't figure out the following problem). When the cmd
module is commanded to exit I get:
(Cmd) exit
Shutting down client...
Traceback (most recent call last):
File "client_loop.py", line 10, in <module>
loop.run_until_complete(test.cmdloop())
File "...\asyncio\base_events.py", line 467, in run_until_complete
future = tasks.ensure_future(future, loop=self)
File "...\lib\asyncio\tasks.py", line 526, in ensure_future
raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
TypeError: An asyncio.Future, a coroutine or an awaitable is required
I'm not good with asyncio
yet, what am I doing wrong? Sorry for the long question but the files/errors make it long and hopefully makes it easier to debug.
Here are the supporting files:
shell.py
# implementation of the (Cmd) prompt with history functionality
# standard imports
import cmd as cmd
class Shell(cmd.Cmd):
def __init__(self, **kwargs):
cmd.Cmd.__init__(self, **kwargs)
self.eventloop = None
self.shutdown_client = None
self.tcp_echo_client = None
def set_eventloop(self, loop):
self.eventloop = loop
def set_funcs(self, tcp_echo_client, shutdown_client):
self.tcp_echo_client = tcp_echo_client
self.shutdown_client = shutdown_client
def do_exit(self,*args):
"""
Exits the shell gracefully
:param args:
:return:
"""
print('Shutting down client...')
self.shutdown_client(self.eventloop)
return True
def default(self, line):
try:
self.eventloop.run_until_complete(self.tcp_echo_client(line, self.eventloop))
except SystemExit:
pass
server.py
# server logic to parse arguments coming over the TCP socket and echo it back
# standard imports
import asyncio
async def handle_echo(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
print("Send: %r" % message)
writer.write(data)
await writer.drain()
print("Close the client socket")
writer.close()
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, '127.0.0.1', 8888, loop=loop)
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
finally:
# Close the server
for task in asyncio.Task.all_tasks():
loop.run_until_complete(task)
server.close()
loop.run_until_complete(server.wait_closed())
loop.stop()
loop.close()
exit(0)
client.py
# client functions to send message over TCP and process response
# standard imports
import asyncio
# user imports
import shell
async def tcp_echo_client(message, loop):
reader, writer = await asyncio.open_connection('127.0.0.1', 8888,
loop=loop)
print('Send: %r' % message)
writer.write(message.encode())
data = await reader.read(100)
print('Received: %r' % data.decode())
print('Close the socket')
writer.close()
def shutdown_client(loop):
loop.stop()
# Find all running tasks:
pending = asyncio.Task.all_tasks()
# Run loop until tasks done:
loop.run_until_complete(asyncio.gather(*pending))
loop = asyncio.get_event_loop()
test = shell.Shell()
test.set_eventloop(loop)
test.set_funcs(tcp_echo_client, shutdown_client)
loop.run_until_complete(test.cmdloop())
loop.close()