micropython-async
micropython-async copied to clipboard
Tutorial - gather() - observing different behaviour
Following your tutorial, you have mentioned that gather can return exceptions with the correct flag set.
However, using the code you provide in section 3.3 gather on MicroPython 1.17, I am entering the asyncio.CancelledError catch block even with the flag set. I believe this is contradictory to the behaviour you described with the return_exceptions=True argument?
>>> %Run -c $EDITOR_CONTENT
Start cancellable bar()
Start barking
Start timeout coro foo()
About to cancel bar
Cancelled
Result: None
If I simply set bar() to return without being cancelled (code below for completeness sake, but I'm sure you get the idea), I do get the intended behaviour where the timeout error is included in the return collection.
I'm a total microcontroller (and Python) rookie, so I haven't done a whole lot of digging to understand the root cause myself, sorry.
try:
import uasyncio as asyncio
except ImportError:
import asyncio
async def barking(n):
print('Start barking')
for _ in range(6):
await asyncio.sleep(1)
print('Done barking.')
return 2 * n
async def foo(n):
print('Start timeout coro foo()')
while True:
await asyncio.sleep(1)
n += 1
return n
async def bar(n):
print('Start cancellable bar()')
return n
async def do_cancel(task):
await asyncio.sleep(5)
print('About to cancel bar')
task.cancel()
async def main():
tasks = [asyncio.create_task(bar(70))]
tasks.append(barking(21))
tasks.append(asyncio.wait_for(foo(10), 7))
asyncio.create_task(do_cancel(tasks[0]))
res = None
try:
res = await asyncio.gather(*tasks, return_exceptions=True)
except asyncio.TimeoutError: # These only happen if return_exceptions is False
print('Timeout') # With the default times, cancellation occurs first
except asyncio.CancelledError:
print('Cancelled')
print('Result: ', res)
asyncio.run(main())
>>> %Run -c $EDITOR_CONTENT
Start cancellable bar()
Start barking
Start timeout coro foo()
About to cancel bar
Done barking.
Result: [70, 42, TimeoutError()]
Well spotted. That is odd, I think a bug must have crept into uasyncio as that script did once work. It still does under CPython. This is the script from the tutorial run under CPython. The outcome is correct:
adminpete@debian8:/mnt/qnap2/temp$ python3
Python 3.8.0 (default, Dec 6 2019, 16:20:13)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import rats40
Start cancellable bar()
Start barking
Start timeout coro foo()
About to cancel bar
Done barking.
Result: [CancelledError(), 42, TimeoutError()]
>>>
whereas, as you point out, under MicroPython it produces:
>>> import rats40
Start cancellable bar()
Start barking
Start timeout coro foo()
About to cancel bar
Cancelled
Result: None
>>>
If I adapt the script so that return_exceptions=False and run it under CPython the outcome is the same as MP:
adminpete@debian8:/mnt/qnap2/temp$ python3
Python 3.8.0 (default, Dec 6 2019, 16:20:13)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import rats40
Start cancellable bar()
Start barking
Start timeout coro foo()
About to cancel bar
Cancelled
Result: None
>>>
I will try to produce a minimum test case whose behaviour differs between CPython and MicroPython and raise an issue.
I have raised this issue with a simplified script.
Nice! I thought of doing the same but wanted to run it past a pro first.
+1 for actually running example code, eh?
Closing as this issue has been fixed by the maintainers.