{"action":"created","issue":{"url":"https://api.github.com/repos/python/asyncio/issues/465","repository_url":"https://api.github.com/repos/python/asyncio","labels_url":"https://api.github.com/repos/python/asyncio/issues/465/labels{/name}","comments_url":"https://api.github.com/repos/python/asyncio/issues/465/comments","events_url":"https://api.github.com/repos/python/asyncio/issues/465/events","html_url":"https://github.com/python/asyncio/pull/465","id":189188609,"number":465,"title":"Add asyncio.run() function.","user":{"login":"1st1","id":239003,"avatar_url":"https://avatars.githubusercontent.com/u/239003?v=3","gravatar_id":"","url":"https://api.github.com/users/1st1","html_url":"https://github.com/1st1","followers_url":"https://api.github.com/users/1st1/followers","following_url":"https://api.github.com/users/1st1/following{/other_user}","gists_url":"https://api.github.com/users/1st1/gists{/gist_id}","starred_url":"https://api.github.com/users/1st1/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/1st1/subscriptions","organizations_url":"https://api.github.com/users/1st1/orgs","repos_url":"https://api.github.com/users/1st1/repos","events_url":"https://api.github.com/users/1st1/events{/privacy}","received_events_url":"https://api.github.com/users/1st1/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":35,"created_at":"2016-11-14T18:29:54Z","updated_at":"2016-11-16T18:47:32Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/python/asyncio/pulls/465","html_url":"https://github.com/python/asyncio/pull/465","diff_url":"https://github.com/python/asyncio/pull/465.diff","patch_url":"https://github.com/python/asyncio/pull/465.patch"},"body":"This PR adds two new APIs: `asyncio.run()` and `asyncio.run_in_executor()`. Ideally, if possible, I'd like to have them in 3.6. If I have a green light on the idea, I'll update the patch to add unittests.\r\n\r\nOne of the main complaints that users have about asyncio is the situation around the event loop. We currently do a poor job explaining how exactly it should be used and how to structure asyncio programs in general.\r\n\r\nWith the recent update of `asyncio.get_event_loop()` we can now explain people that passing event loop explicitly is unnecessary, and that library APIs should be designed around coroutines.\r\n\r\nI think we need to add two more functions to make the loop disappear from most asyncio programs.\r\n\r\n-----\r\n\r\n`asyncio.run_in_executor()` coroutine: maps directly to the equivalent `loop.run_in_executor()`. The idea is that people don't need an event loop to use the function:\r\n\r\n```python\r\nasync def coro():\r\n    await asyncio.run_in_executor(None, calculate, 100)\r\n```\r\n\r\n----\r\n\r\n`asyncio.run()` function: run a coroutine taking care of the asyncio event loop.\r\n\r\n**Pros:**\r\n\r\n* Simplification of the documentation: I'm working on an update, and one of the things that bothers me that to each and every example have to have a piece of code that manages the loop.  For example:\r\n\r\n    ```python\r\n    async def hello():\r\n        await asyncio.sleep(1)\r\n        print('hello')\r\n\r\n    loop = asyncio.get_event_loop()\r\n    loop.run_until_complete(hello())\r\n    loop.close()\r\n    ```\r\n\r\n    The problem is that the above snippet isn't fully correct, we should have a `try-finally` block to ensure that `loop.close()` is always called. Even in the docs we don't do that.  With `asyncio.run()` the snippet becomes much shorter:\r\n\r\n    ```python\r\n    async def hello():\r\n        await asyncio.sleep(1)\r\n        print('hello')\r\n\r\n    asyncio.run(hello())\r\n    ```\r\n\r\n* It's currently hard to experiment with asyncio in the REPL, because `get_event_loop` and `run_until_complete` are rather long names to type. With `asyncio.run()`:\r\n\r\n    ```pycon\r\n    >>> from asyncio import *\r\n    >>> async def foo():\r\n    ...     await sleep(1)\r\n    >>> run(foo())\r\n    ```\r\n\r\n    And `asyncio.run()` can be called multiple times.\r\n\r\n* Asynchronous generators are properly cleaned-up. `loop.shutdown_asyncgens()` is a somewhat low-level advanced API, and I expect something that a lot of people will forget to use.\r\n\r\n* The function promotes a coroutine-centric design.  In many cases, is is possible to bootstrap an asyncio program with just one coroutine.\r\n\r\n**Cons:**\r\n\r\n* It's not possible to completely substitute `loop.run_forever()`.  One of the documented patterns in asyncio is to use `run_forever` to bootstrap servers:\r\n\r\n    ```python\r\n    loop = asyncio.get_event_loop()\r\n    server = loop.run_until_complete(\r\n        asyncio.start_server(handle_echo, '127.0.0.1', 8888, loop=loop))\r\n\r\n    try:\r\n        loop.run_forever()\r\n    except KeyboardInterrupt:\r\n        pass\r\n\r\n    # Close the server\r\n    server.close()\r\n    loop.run_until_complete(server.wait_closed())\r\n    loop.close()\r\n    ```\r\n\r\n    To support cases like this, we'll need to add another API.  One of the ideas that I have (**not for 3.6!**) is to add `asyncio.forever()` awaitable, so that the above example could be translated to:\r\n\r\n    ```python\r\n    async def main():\r\n        server = await asyncio.start_server(handle_echo, '127.0.0.1', 8888)\r\n\r\n        try:\r\n            await asyncio.forever()\r\n        except KeyboardInterrupt:\r\n            pass\r\n\r\n        server.close()\r\n        await server.wait_closed()\r\n\r\n    asyncio.run(main())\r\n    ```\r\n\r\n    Adding `asyncio.forever()` would require us to add new APIs to event loop, and it is something that clearly requires a thorough review process (I'm thinking about writing a PEP).\r\n\r\n    However, we can probably add `asyncio.run()` function in 3.6 to cover some use cases, and enhance it further in 3.7.\r\n\r\n@gvanrossum, what do you think?"},"comment":{"url":"https://api.github.com/repos/python/asyncio/issues/comments/261035090","html_url":"https://github.com/python/asyncio/pull/465#issuecomment-261035090","issue_url":"https://api.github.com/repos/python/asyncio/issues/465","id":261035090,"user":{"login":"ilevkivskyi","id":12005495,"avatar_url":"https://avatars.githubusercontent.com/u/12005495?v=3","gravatar_id":"","url":"https://api.github.com/users/ilevkivskyi","html_url":"https://github.com/ilevkivskyi","followers_url":"https://api.github.com/users/ilevkivskyi/followers","following_url":"https://api.github.com/users/ilevkivskyi/following{/other_user}","gists_url":"https://api.github.com/users/ilevkivskyi/gists{/gist_id}","starred_url":"https://api.github.com/users/ilevkivskyi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ilevkivskyi/subscriptions","organizations_url":"https://api.github.com/users/ilevkivskyi/orgs","repos_url":"https://api.github.com/users/ilevkivskyi/repos","events_url":"https://api.github.com/users/ilevkivskyi/events{/privacy}","received_events_url":"https://api.github.com/users/ilevkivskyi/received_events","type":"User","site_admin":false},"created_at":"2016-11-16T18:47:32Z","updated_at":"2016-11-16T18:47:32Z","body":"@1st1\r\n> I'm not sure we need to push more features to asyncio.run. So far I see two options:\r\n\r\nI would really prefer option 2: two separate functions -- ``run`` for coroutines and ``run_forever`` for asyncgen's. I believe this could avoid confusion with old style coroutines mentioned by Guido."}}
{"actor":{"display_login":"ilevkivskyi"}}