QUERY · ISSUE
uasyncio: Code throws exception under CPython
extmod
MicroPython allows a task to be created before issuing .run() but CPython throws an exception.
try:
import uasyncio as asyncio
except ImportError:
import asyncio
async def foo():
await asyncio.sleep(3)
async def bar():
await foo_task
foo_task = asyncio.create_task(foo()) # Naughty code here...
asyncio.run(bar())
Outcome under CPython 3.8.0:
adminpete@debian8:/mnt/qnap2/temp$ python3 -m rats_x
Traceback (most recent call last):
File "/usr/local/lib/python3.8/runpy.py", line 192, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/local/lib/python3.8/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/mnt/qnap2/temp/rats_x.py", line 12, in <module>
foo_task = asyncio.create_task(foo())
File "/usr/local/lib/python3.8/asyncio/tasks.py", line 381, in create_task
loop = events.get_running_loop()
RuntimeError: no running event loop
sys:1: RuntimeWarning: coroutine 'foo' was never awaited
My view is that this doesn't matter, but I thought it worth reporting.
CANDIDATE · PULL REQUEST
extmod/uasyncio/task.py: Fix crash when non-awaited task is awaited.
extmod
A task that has been sent to the loop's exception handler due to being re-scheduled twice will then subsequently cause a raise None if it is subsequently awaited. In the C version of task.py, this causes a segfault.
async def task():
raise Exception("task")
t = asyncio.create_task(task())
await asyncio.sleep(0)
await asyncio.sleep(0) # <-- loop exception handler called here
await t # <-- this line crashes
This PR makes the await succeed (via raising StopIteration instead).
I'm not sure this is the right fix though... it seems wrong to make the await succeed. The other option that I can think of are:
- Improve the logic for detecting the "un-awaited" task. Without reference counting this seems impossible.
- Make the task retain its exception, and even after it has been delivered to the loop's exception handler, still have await raise the original exception.
- Make the await raise CancelledError.
I think the third option (CancelledError) might be the most pragmatic alternative?