[Async-sig] Adding asyncio.run() function in Python 3.6

Yury Selivanov yselivanov at gmail.com
Wed Nov 16 13:12:42 EST 2016


One of the remaining problems with the event loop in asyncio is bootstrapping/finalizing asyncio programs.

Currently, there are two different scenarios:

[1] Running a coroutine:

    async def main():
       # your program
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

[2] Running a server:

    loop = asyncio.get_event_loop()
    srv = loop.run_until_complete(
      loop.create_server(…))
    try:
      loop.run_forever()
    finally:
      try:
        srv.close()
        loop.run_until_complete(srv.wait_closed())
      finally:
        loop.close()

Both cases are *incomplete*: they don’t do correct finalization of asynchronous generators. To do that we’ll need to add another 1-3 lines of code (extra try-finally).

This manual approach is really painful: 

* It makes bootstrapping asyncio code unnecessarily hard.

* It makes the documentation hard to follow.  And we can’t restructure the docs to cover the loop only in the advanced section.

* Most of the people will never know about `loop.shutdown_asyncgen` coroutine.  

* We don’t have a place to add debug code to let people know that their asyncio program didn’t clean all resources properly (a lot of unordered warnings will be spit out instead).

In https://github.com/python/asyncio/pull/465 I propose to add a new function to asyncio in Python 3.6: asyncio.run().

The function can either accept a coroutine, which solves [1]:

    async def main():
       # your program
    asyncio.run(main())

Or it can accept an asynchronous generator, which solves [2]:

    async def main():
      srv = await loop.create_server(…))
      try:
        yield  # let the loop run forever
      finally:
        srv.close()
        await srv.wait_closed()

    asyncio.run(main())

asyncio.run() solves the following:

* An easy way to start an asyncio program that properly takes care of loop instantiation and finalization.

* It looks much better in the docs.  With asyncio.run people don’t need to care about the loop at all, most probably will never use it.

* Easier to experiment with asyncio in REPL.

* The loop and asynchronous generators will be cleaned up properly.

* We can add robust debug output to the function, listing the unclosed tasks, servers, connections, asynchronous generators etc, helping people with the cleanup logic.

* Later, if we need to add more cleanup code to asyncio, we will have a function to add the logic to.

I feel that we should add this to asyncio.  One of the arguments against that, is that overloading asyncio.run to accept both coroutines and asynchronous generators makes the API more complex.  If that’s really the case, we can add two functions: asyncio.run(coro) and asyncio.run_forever(async_generator).

Also take a look at https://github.com/python/asyncio/pull/465.

Thanks,
Yury


More information about the Async-sig mailing list