Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cleanly exit a python async app with executor tasks still waiting for console input

I'm trying to add a console input listener to an async app, and I've hit an interesting problem.

The following code doesn't terminate when it should, and instead continues waiting for input after the script has terminated:

import asyncio


loop = asyncio.get_event_loop()

async def quit_after(timeout):
    await asyncio.sleep(timeout)
    print('main function terminating now') # this could simulate a crash, or a proper exit

async def print_input_loop():
    while True:
        # executor thread is daemon - should terminate when script ends, but doesn't
        text = await loop.run_in_executor(None, input, '> ')
        print(text)

if __name__ == '__main__':
    loop.create_task(print_input_loop())

    try:
        loop.run_until_complete(quit_after(5))
    finally:
        loop.close()

    print('reached end of file - script should now terminate')

I've looked into other methods for getting input asynchronously, such as using add_reader() on sys.stdin, but this doesn't work on Windows, and I need my solution to be platform independent.

Does anyone know either a better way to get console input asynchronously that will gracefully halt when the app halts, or a way to fix my existing code so that it cleanly exits?

like image 334
Lev Avatar asked Feb 17 '17 02:02

Lev


1 Answers

A possible workaround is to disable the exit function for threads in concurrent.futures:

def disable_exit_for_threadpool_executor():
    import atexit
    import concurrent.futures
    atexit.unregister(concurrent.futures.thread._python_exit)

Also, aioconsole provides a cross-platform function for handling console input in asyncio:

import aioconsole

async def echo_loop():
    while True:
        text = await aioconsole.ainput('> ')
        print(text.strip())
like image 166
Vincent Avatar answered Sep 17 '22 18:09

Vincent