question re: loop.shutdown_asyncgens()
![](https://secure.gravatar.com/avatar/9b513ee7cc400c9b7374f4697a1e69ad.jpg?s=120&d=mm&r=g)
I have a question about PEP 525 (Asynchronous Generators) which I'm sure has a simple answer, but I didn't see it in the PEP or final discussion: https://mail.python.org/pipermail/python-dev/2016-September/146265.html Basically, why is the API such that loop.shutdown_asyncgens() must be called manually? For example, why can't it be called automatically as part of close(), which seems like it would be a friendlier API and more helpful to the common case? I was trying asynchronous iterators in my code and getting the following error: Exception ignored in: <generator object Queue.get at 0x7f950a667678> Traceback (most recent call last): File "/usr/local/lib/python3.6/asyncio/queues.py", line 169, in get getter.cancel() # Just in case getter is not done yet. File "/usr/local/lib/python3.6/asyncio/base_events.py", line 574, in call_soon self._check_closed() File "/usr/local/lib/python3.6/asyncio/base_events.py", line 357, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed Calling loop.shutdown_asyncgens() made the error go away, but it seems a little obscure that by adding an asynchronous iterator somewhere in your code, you have to remember to check that that line is present before loop.close() is called (and the exception message doesn't provide a good hint). Is there any disadvantage to always calling loop.shutdown_asyncgens() (i.e. even if it's not needed)? And why might someone need to call it at a different time? Thanks, --Chris
![](https://secure.gravatar.com/avatar/60a2f1855ca0d8aac3fa75a57233a3f1.jpg?s=120&d=mm&r=g)
One of the design decisions about `loop.close()` is that it doesn't do a single event loop iteration, making its behaviour highly predictable. To make `loop.close()` to run `loop.shutdown_asyncgens()` (which is a coroutine), we would have needed to change that. One of the ways we want to mitigate this problem in Python 3.7 is to add a new function to bootstrap asyncio and run top-level coroutines: `asyncio.run()`. You can read more about it here: [1]. I'm working on a new PEP that will summarize asyncio changes in 3.7. I don't have a concrete ETA for it, but I'll try to get the first draft out by mid September. [1] https://github.com/python/asyncio/pull/465 Thanks, Yury On Jul 27, 2017, 11:24 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
I have a question about PEP 525 (Asynchronous Generators) which I'm sure has a simple answer, but I didn't see it in the PEP or final discussion: https://mail.python.org/pipermail/python-dev/2016-September/146265.html
Basically, why is the API such that loop.shutdown_asyncgens() must be called manually? For example, why can't it be called automatically as part of close(), which seems like it would be a friendlier API and more helpful to the common case?
I was trying asynchronous iterators in my code and getting the following error:
Exception ignored in: <generator object Queue.get at 0x7f950a667678 Traceback (most recent call last): File "/usr/local/lib/python3.6/asyncio/queues.py", line 169, in get getter.cancel() # Just in case getter is not done yet. File "/usr/local/lib/python3.6/asyncio/base_events.py", line 574, in call_soon self._check_closed() File "/usr/local/lib/python3.6/asyncio/base_events.py", line 357, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed
Calling loop.shutdown_asyncgens() made the error go away, but it seems a little obscure that by adding an asynchronous iterator somewhere in your code, you have to remember to check that that line is present before loop.close() is called (and the exception message doesn't provide a good hint).
Is there any disadvantage to always calling loop.shutdown_asyncgens() (i.e. even if it's not needed)? And why might someone need to call it at a different time?
Thanks, --Chris _______________________________________________ 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/
![](https://secure.gravatar.com/avatar/9b513ee7cc400c9b7374f4697a1e69ad.jpg?s=120&d=mm&r=g)
Thanks, Yury. Have you also considered including recommended setup / cleanup boilerplate in a place where it's easy for asyncio users to find, like in the asyncio docs here? https://docs.python.org/3/library/asyncio-eventloop.html#run-an-event-loop One example of a Python module using this approach is itertools: https://docs.python.org/3/library/itertools.html#itertools-recipes Currently, even the example snippet provided for loop.shutdown_asyncgens(): https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEve... is incomplete because it doesn't execute shutdown_asyncgens() in a try-finally like you do in your latest patch posted on PR #465. Also, even if run() is added to Python 3.7, Python 3.6 users would still need / benefit from being able to find blessed boilerplate in a central place. Thanks, --Chris On Thu, Jul 27, 2017 at 8:40 PM, Yury Selivanov <yselivanov@gmail.com> wrote:
One of the design decisions about `loop.close()` is that it doesn't do a single event loop iteration, making its behaviour highly predictable. To make `loop.close()` to run `loop.shutdown_asyncgens()` (which is a coroutine), we would have needed to change that.
One of the ways we want to mitigate this problem in Python 3.7 is to add a new function to bootstrap asyncio and run top-level coroutines: `asyncio.run()`. You can read more about it here: [1].
I'm working on a new PEP that will summarize asyncio changes in 3.7. I don't have a concrete ETA for it, but I'll try to get the first draft out by mid September.
[1] https://github.com/python/asyncio/pull/465
Thanks, Yury
On Jul 27, 2017, 11:24 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
I have a question about PEP 525 (Asynchronous Generators) which I'm sure has a simple answer, but I didn't see it in the PEP or final discussion: https://mail.python.org/pipermail/python-dev/2016-September/146265.html
Basically, why is the API such that loop.shutdown_asyncgens() must be called manually? For example, why can't it be called automatically as part of close(), which seems like it would be a friendlier API and more helpful to the common case?
I was trying asynchronous iterators in my code and getting the following error:
Exception ignored in: <generator object Queue.get at 0x7f950a667678 Traceback (most recent call last): File "/usr/local/lib/python3.6/asyncio/queues.py", line 169, in get getter.cancel() # Just in case getter is not done yet. File "/usr/local/lib/python3.6/asyncio/base_events.py", line 574, in call_soon self._check_closed() File "/usr/local/lib/python3.6/asyncio/base_events.py", line 357, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed
Calling loop.shutdown_asyncgens() made the error go away, but it seems a little obscure that by adding an asynchronous iterator somewhere in your code, you have to remember to check that that line is present before loop.close() is called (and the exception message doesn't provide a good hint).
Is there any disadvantage to always calling loop.shutdown_asyncgens() (i.e. even if it's not needed)? And why might someone need to call it at a different time?
Thanks, --Chris _______________________________________________ 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/
![](https://secure.gravatar.com/avatar/60a2f1855ca0d8aac3fa75a57233a3f1.jpg?s=120&d=mm&r=g)
Thanks, Yury On Jul 28, 2017, 4:38 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
Thanks, Yury. Have you also considered including recommended setup / cleanup boilerplate in a place where it's easy for asyncio users to find, like in the asyncio docs here? https://docs.python.org/3/library/asyncio-eventloop.html#run-an-event-loop
Yes, a PR would be welcome!
One example of a Python module using this approach is itertools: https://docs.python.org/3/library/itertools.html#itertools-recipes
Currently, even the example snippet provided for loop.shutdown_asyncgens(): https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEve... is incomplete because it doesn't execute shutdown_asyncgens() in a try-finally like you do in your latest patch posted on PR #465.
Also, even if run() is added to Python 3.7, Python 3.6 users would still need / benefit from being able to find blessed boilerplate in a central place.
I was going to release a new module on PyPI called "asyncio_next" or something with backports (and to experiment with the proposed APIs before 3.7 is out). Yury
![](https://secure.gravatar.com/avatar/9b513ee7cc400c9b7374f4697a1e69ad.jpg?s=120&d=mm&r=g)
Thanks! I'll try to find time to propose a PR. Also, for suggestions around the new API, would you prefer that be posted to PR #465, or can it be done here? --Chris On Fri, Jul 28, 2017 at 1:45 PM, Yury Selivanov <yselivanov@gmail.com> wrote:
Thanks, Yury
On Jul 28, 2017, 4:38 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
Thanks, Yury. Have you also considered including recommended setup / cleanup boilerplate in a place where it's easy for asyncio users to find, like in the asyncio docs here? https://docs.python.org/3/library/asyncio-eventloop.html#run-an-event-loop
Yes, a PR would be welcome!
One example of a Python module using this approach is itertools: https://docs.python.org/3/library/itertools.html#itertools-recipes
Currently, even the example snippet provided for loop.shutdown_asyncgens(): https://docs.python.org/3/library/asyncio-eventloop.html#asyncio. AbstractEventLoop.shutdown_asyncgens is incomplete because it doesn't execute shutdown_asyncgens() in a try-finally like you do in your latest patch posted on PR #465.
Also, even if run() is added to Python 3.7, Python 3.6 users would still need / benefit from being able to find blessed boilerplate in a central place.
I was going to release a new module on PyPI called "asyncio_next" or something with backports (and to experiment with the proposed APIs before 3.7 is out).
Yury
![](https://secure.gravatar.com/avatar/60a2f1855ca0d8aac3fa75a57233a3f1.jpg?s=120&d=mm&r=g)
On Jul 28, 2017, 4:57 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
Thanks! I'll try to find time to propose a PR.
Also, for suggestions around the new API, would you prefer that be posted to PR #465, or can it be done here?
I think we can discuss it here, but up to you. Yury
![](https://secure.gravatar.com/avatar/9b513ee7cc400c9b7374f4697a1e69ad.jpg?s=120&d=mm&r=g)
On Fri, Jul 28, 2017 at 1:58 PM, Yury Selivanov <yselivanov@gmail.com> wrote:
On Jul 28, 2017, 4:57 PM -0400, Chris Jerdonek <chris.jerdonek@gmail.com>, wrote:
Thanks! I'll try to find time to propose a PR.
Also, for suggestions around the new API, would you prefer that be posted to PR #465, or can it be done here?
I think we can discuss it here, but up to you.
So here are some of my thoughts related to exposing an API for asyncio.run() and friends: I think it would be helpful if, in addition to asyncio.run(), the API made some attempt to expose building blocks (where natural) so that if the user wants to do something slightly different from what run() supports, they don't need to copy from the internal implementation. I'm including one suggestion for this below. Related to this, I'd like to ask that the following two use cases be contemplated / relatively easy to support through the API. It doesn't need to be through the top-level functions like run(), but maybe by using the building blocks: 1) creating loops "on-the-fly" in different threads, like I asked about in this thread: https://mail.python.org/pipermail/async-sig/2017-July/000348.html The PR #465 discussion currently seems to be leaning away from supporting this in any way. 2) creating fresh loops for individual unittest TestCase methods / subTests, without mandating a specific approach. Different approaches that I think should be supportable through the API include user-implemented TestCase method decorators that create and destroy a loop using the API, as well as doing this through TestCase's setUp() and tearDown(). One "building block" that I think should be exposed is a context manager for managing the lifetime of a loop. It could look something like this: @contextmanager def new_loop(debug=False): loop = events.new_event_loop() try: events.set_event_loop(loop) if debug: loop.set_debug(True) yield loop finally: _cleanup(loop) Both run() and run_forever() in the latest patch posted on PR #465 can be implemented in terms of this context manager without changing the logic: https://github.com/python/asyncio/pull/465/commits/275072a2fbae0c98619597536... Even if it was decided not to make this context manager public, I think the implementations of run() / run_forever() / etc. would benefit by making the commonalities between them clearer, etc. Thanks, --Chris
participants (2)
-
Chris Jerdonek
-
Yury Selivanov