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
_______________________________________________
Async-sig mailing list
Async-sig@python.org
https://mail.python.org/mailman/listinfo/async-sig
Code of Conduct: https://www.python.org/psf/codeofconduct/

Reply via email to