[Python-ideas] Add ability to get current event loop within asyncio coroutine

77 views
Skip to first unread message

Ilya Kulakov

unread,
May 19, 2016, 2:22:26 PM5/19/16
to python-ideas
asyncio is a great library which with recent changes in 3.5 was made even better.

However there is an inconvenience that bothers me and looks very unnatural: inability to access event loop
that executes a coroutine from within that coroutine.

This is unnatural, because we have `self` and `cls` to access object from a method it is bound to,
we have `current_thread` to get an instance of Thread that currently executes code, but for a coroutine
we have no such method, we cannot get its context of execution.
Current implementation of `get_event_loop` method is not sufficient, it will not work if thread has more
than one event loop.

I think Python would benefit from providing either a new method (e.g. `asyncio.current_event_loop`) or modifying `asyncio.get_event_loop`
to return current event loop when called from a coroutine. It should, in my opinion, reduce necessity of passing event loop
between coroutines in application's code as it is done in asyncio itself and 3rd party libraries (like aiohttp).

With this change, the following example from docs:

    async def display_date(loop):
        end_time = loop.time() + 5.0
        while True:
            print(datetime.datetime.now())
            if (loop.time() + 1.0) >= end_time:
                break
            await asyncio.sleep(1, loop=loop)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(display_date(loop))
    loop.close()

Could be rewritten as:

    async def display_date():
        loop = asyncio.current_event_loop()
        end_time = loop.time() + 5.0
        while True:
            print(datetime.datetime.now())
            if (loop.time() + 1.0) >= end_time:
                break
            await asyncio.sleep(1, loop=loop)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(display_date(loop))
    loop.close()

Ilya Kulakov

unread,
May 19, 2016, 2:50:45 PM5/19/16
to python...@python.org
asyncio is a great library which with recent changes in 3.5 was made even better.

However there is an inconvenience that bothers me and looks very unnatural: inability to access event loop
that executes a coroutine from within that coroutine.

This is unnatural, because we have `self` and `cls` to access object from a method it is bound to,
we have `current_thread` to get an instance of Thread that currently executes code, but for a coroutine
we have no such method, we cannot get its context of execution.
Current implementation of `get_event_loop` method is not sufficient, it will not work if thread has more
than one event loop.

I think Python would benefit from providing either a new method (e.g. `asyncio.current_event_loop`) or modifying `asyncio.get_event_loop`
to return current event loop when called from a coroutine. It should, in my opinion, reduce necessity of passing event loop
between coroutines in application's code as it is done in asyncio itself and 3rd party libraries (like aiohttp).


Best Regards,
Ilya Kulakov

Guido van Rossum

unread,
May 19, 2016, 3:11:02 PM5/19/16
to Ilya Kulakov, Python-Ideas
Can you explain why asyncio.get_event_loop() doesn't do what you want?
> _______________________________________________
> Python-ideas mailing list
> Python...@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/



--
--Guido van Rossum (python.org/~guido)
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Ian Kelly

unread,
May 19, 2016, 3:59:42 PM5/19/16
to Python-Ideas
On Thu, May 19, 2016 at 12:49 PM, Ilya Kulakov <kulako...@gmail.com> wrote:
> Current implementation of `get_event_loop` method is not sufficient, it will
> not work if thread has more
> than one event loop.

Why do you need more than one event loop per thread? You couldn't run
more than one of them at a time.

> I think Python would benefit from providing either a new method (e.g.
> `asyncio.current_event_loop`) or modifying `asyncio.get_event_loop`
> to return current event loop when called from a coroutine. It should, in my
> opinion, reduce necessity of passing event loop
> between coroutines in application's code as it is done in asyncio itself and
> 3rd party libraries (like aiohttp).

To me this sounds like a case for a custom event loop policy, which is
what determines the behavior of get_event_loop.

Vincent Michel

unread,
May 20, 2016, 5:03:23 AM5/20/16
to python...@python.org

On Thu, May 19, 2016 at 11:49 AM, Ilya Kulakov <kulako...@gmail.com>
wrote:
> I think Python would benefit from providing either a new method (e.g.
> `asyncio.current_event_loop`) or modifying `asyncio.get_event_loop`
> to return current event loop when called from a coroutine. It should, in my
> opinion, reduce necessity of passing event loop
> between coroutines in application's code as it is done in asyncio itself and
> 3rd party libraries (like aiohttp).

I was about to make the same proposal. I can add two practical arguments
in favor.

First, some coroutines could be simplified. For instance, asyncio.sleep
takes an optional loop argument, defaulting to get_event_loop(). But
get_event_loop is called inside the coroutine, and therefore inside a
running event loop. This loop is actually the only valid choice, and
using anything else should raise an exception. If get_event_loop is
guaranteed to return the current running loop, then there is no reason
to pass it as an argument to asyncio.sleep. Other coroutines could
benefit from that, including wait and wait_for.

It would also help for queues, locks and other task functions (gather,
shield, as_completed, etc.). Here the loop argument is still useful, in
case those objects are declared outside their event loop. However, it
would be really convenient to assume loop=None always works as expected
if the object is created inside a coroutine. In many cases, libraries
tend to forward the loop argument from coroutine to coroutine, just in
case someone writes:

loop = asyncio.new_event_loop()
loop.run_until_complete(my_coro(loop=loop))

instead of:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(my_coro())

TL;DR
`async def coro(*, loop=None)` doesn't really make sense, and could be
discouraged if get_event_loop was guaranteed to return the running event
loop when called inside a coroutine.

Guido van Rossum

unread,
May 20, 2016, 12:23:44 PM5/20/16
to Vincent Michel, Python-Ideas
OK, I finally see the point. There are legitimate situations where
get_event_loop() returns None (this is how most of asyncio's tests are
running). So we want a separate function, e.g.
get_current_event_loop(). Send a PR to the GitHub project.
--
--Guido van Rossum (python.org/~guido)

Ilya Kulakov

unread,
May 20, 2016, 1:54:40 PM5/20/16
to gu...@python.org, Vincent Michel, Python-Ideas
On 20 May 2016, at 12:22, Guido van Rossum <gu...@python.org> wrote:

OK, I finally see the point. There are legitimate situations where
get_event_loop() returns None (this is how most of asyncio's tests are
running). 

Unless there are tests that expect that get_event_loop() would return None
when called from within coroutine it should be fine.


So we want a separate function, e.g.
get_current_event_loop(). Send a PR to the GitHub project.

Also see related bug: http://bugs.python.org/issue26969
Yuri did some work, but he argumented in favor of modifying get_event_loop.

Should we continue discussion of implementation here or in that issue?

Guido van Rossum

unread,
May 20, 2016, 2:05:30 PM5/20/16
to Ilya Kulakov, Vincent Michel, Python-Ideas
On Fri, May 20, 2016 at 10:53 AM, Ilya Kulakov <kulako...@gmail.com> wrote:
>
> On 20 May 2016, at 12:22, Guido van Rossum <gu...@python.org> wrote:
>
> > OK, I finally see the point. There are legitimate situations where
> > get_event_loop() returns None (this is how most of asyncio's tests are
> > running).
>
>
> Unless there are tests that expect that get_event_loop() would return None
> when called from within coroutine it should be fine.

The tests are supposed to fail if something doesn't explicitly pass
the loop; this is how we test that all code in asyncio itself always
correctly passes the loop rather than accidentally relying on
get_event_loop().

> > So we want a separate function, e.g.
> > get_current_event_loop(). Send a PR to the GitHub project.
>
>
> Also see related bug: http://bugs.python.org/issue26969
> Yuri did some work, but he argumented in favor of modifying get_event_loop.
>
> Should we continue discussion of implementation here or in that issue?

I consider it a feature that you can make get_event_loop() return
None. But I think it would be a bug (in the Policy class) if it
returned some other loop that's not the one that's running.

I also think it's fine for a particular library or app to request that
get_event_loop() not return None. User code is simpler that way.
Reply all
Reply to author
Forward
0 new messages