8

The is the simple test code and the result.

import asyncio

async def test():
    await asyncio.sleep(1)

if __name__ == '__main__':

    asyncio.set_event_loop(None)      # Clear the main loop.
    loop = asyncio.new_event_loop()   # Create a new loop.
    loop.run_until_complete(test())   # Run coroutine over the new loop

Traceback (most recent call last):
  File "test_test.py", line 11, in <module>
    loop.run_until_complete(test())
  File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
    return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "test_test.py", line 5, in test
    await asyncio.sleep(1)
  File "/usr/lib/python3.5/asyncio/tasks.py", line 510, in sleep
    loop = events.get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 632, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 578, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'MainThread'.

I run the async def test() over the new loop and expected that asyncio.sleep(1) which is nested by test() also use the new loop.

In contrast to that, sleep() still seems to access main loop I set as None.

I know I can re-set a main loop as the new loop with asyncio.set_event_loop(loop) before calling run_until_complete() and it will work with no exception.

However, I want to know it is normal for asyncio that main loop Must be set and is used for coroutines regardless of a loop over which coroutine is run.

Andrew Svetlov
  • 16,730
  • 8
  • 66
  • 69
SangminKim
  • 8,358
  • 14
  • 69
  • 125

1 Answers1

6

I want to know it is normal for asyncio that main loop Must be set and is used for coroutines regardless of a loop over which coroutine is run.

This used to be required prior to Python 3.6. The reason is that functions like asyncio.sleep() need an event loop to be able to use loop.call_later() to schedule a wake-up call to complete the future.

As of Python 3.6 (or 3.5.3, which included a fix for the issue), when get_event_loop() is invoked from a coroutine driven by an event loop, it always returns the event loop that drives it. As a result, your code works correctly.

The new behavior is not mentioned in the online documentation, but is in the docstring:

When called from a coroutine or a callback (e.g. scheduled with call_soon or similar API), this function will always return the running event loop.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • 1
    Please replace 3.7 with 3.5.3 – Andrew Svetlov Feb 04 '18 at 23:10
  • @Andrew Svetlov, it seems to work with `python 3.5.3` but where can I find the fixed content on this issue? I couldn't find any mention about this on [release note](https://www.python.org/downloads/release/python-353/) – SangminKim Feb 05 '18 at 06:59
  • @AndrewSvetlov Replacing 3.7 with 3.5.3 wouldn't explain why the snippet fails on 3.6. Python 3.7 was the first version to document the new behavior of `get_event_loop()`, and I've seen it mentioned in various places as a 3.7 innovation. It might be interesting to find out why the OP's code works in 3.5.3, but that doesn't change the answer to the OP's more generic question of whether it is required to set an event loop to use asyncio. – user4815162342 Feb 05 '18 at 08:50
  • @AndrewSvetlov I now see that the change was treated [as a bugfix](https://bugs.python.org/issue28613) and backported to bugfix branches. I've now updated the answer to link to the concrete issue. Thanks for the correction. – user4815162342 Feb 05 '18 at 12:55
  • 2
    Actually the feature was introduced in Python 3.6 and backported to 3.5.3. The snippet uses 3.5, I guess 3.5.2. – Andrew Svetlov Feb 05 '18 at 16:42
  • @AndrewSvetlov You're right. The remaining question of why it failed for me in 3.6 is explained by the fact that my Ubuntu 16.10 comes with the 3.6.0b2 pre-release. – user4815162342 Feb 05 '18 at 19:41
  • 1
    @SangminKim The change is in the [full 3.5 change log](https://docs.python.org/3.5/whatsnew/changelog.html#python-3-5-3-release-candidate-1), look for `bpo-28613`. – user4815162342 Feb 05 '18 at 21:32
  • The [in the documentation](https://docs.python.org/3.7/library/asyncio-eventloops.html#event-loop-policy-interface) link is dead. Is this the correct refertence? [new in the documentation](https://docs.python.org/3.7/library/asyncio-eventloop.html#asyncio-event-loop) – Josiah L. Aug 22 '19 at 19:35
  • @JosiahL. The quoted text appears to have been lost in the recent overhaul of the documentation. What's bad is that I can't find the equivalent statement (that documents that `get_event_loop` works like `get_running_loop` if the loop is actually running) in the new text. – user4815162342 Aug 22 '19 at 21:05