QUERY · ISSUE
uasyncio emits no error message if a coro is called rather than awaited.
extmod
The following
import uasyncio as asyncio
async def bar():
await asyncio.sleep(1)
print('In bar')
async def foo():
print('Launching bar')
await bar()
print('Getting it wrong')
bar() # bad
loop = asyncio.get_event_loop()
loop.run_until_complete(foo())
produces this output
>>> import rats37
Launching bar
In bar
Getting it wrong
>>>
CPython 3.5.2 issues the following error message:
>>> import rats37
Launching bar
In bar
Getting it wrong
/home/adminpete/rats37.py:11: RuntimeWarning: coroutine 'bar' was never awaited
bar()
>>>
It would be useful if a message were emitted in response to this error.
CANDIDATE · ISSUE
uasyncio problem with call_soon and args
I'm probably missing something but this worked until I introduced an arg:
import uasyncio as asyncio
async def foo(n):
arg = await asyncio.sleep(1)
print("foo {} run {}".format(n, arg))
loop = asyncio.get_event_loop()
arg = 42
loop.call_soon(foo(1), arg)
loop.run_forever()
Outcome (Pyboard and Unix)
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
File "/sd/lib/uasyncio/core.py", line 115, in run_forever
File "/sd/lib/uasyncio/core.py", line 79, in run_forever
TypeError: can't send non-None value to a just-started generator
>>>
call_soon() accepts a callback, not a coroutine: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.call_soon
I'm aware that this is the case in CPython. Things in the code led me to believe that MicroPython deviated from this rule. Firstly a comment in core.py says that ensure_future and Task are deprecated. Further, the implementation these execute call_soon; in these cases call_soon will be fed a coro.
Is EventLoop.create_task the one correct way to launch a coro?
Another question. What is the approved way for a coro to yield to the scheduler efficiently in the case where no specific delay is required (round-robin scheduling)? The MicroPython implementation allows
yieldwhich works, but would throw a syntax error in CPython.A point arose when I ported a demo program from my scheduler. It flashes the Pyboard LED's asynchronously. It is intended to run for a period then stop, which it does via run_until_complete. However if I run it again, the LED's don't flash (unless I soft reset and re-import). To get round this I needed to add an interlock whereby the timing coro flags each flashing coro to terminate. Each reports completion back to the timing routine, allowing it to complete causing the event loop to shut down. This works but is a bit involved for a trivial demo :)
Is there a clean way to shut down an asyncio program so it can be run again at the REPL?
Sorry if I'm asking dumb questions. I'm trying to understand the MicroPython way of using asyncio for hardware interfacing with a view to rendering my scheduler redundant. My plan (once I've got to grips with it) is to write a tutorial which might pre-empt dumb questions from others.
Yes. The fact that create_task calls call_soon (actually call_at) behind the scenes and passes a coro to it is just an implementation detail. You shouldn't rely on this fact; call_soon should only be fed callbacks by the end-user.
OK, thanks. What about issuing
yieldin coros? It's faster thanawait asyncio.sleep(0);yield nis also more efficient thanawait asyncio.sleep_ms(n). However these constructs are not allowed in CPython. Will MicroPython continue to supportyieldin coros?Secondly, where an asyncio program runs to completion I find that a soft reset is required to run it again unless I include code to ensure every coro completes before the program terminates. This isn't a major issue in that most embedded applications run forever but I'd like to understand why this occurs. And whether this is a bug or a feature to be documented.
I would suggest using
await asyncio.sleep(0)for the time being, since that's guaranteed to continue working. Anything else will depend on how asyncio looks when it's more mature.It sounds like there is some left-over state in the uasyncio scheduler. Did you create a new event loop for the second run, or reuse the existing one?
If you paste the following on a Pyboard at the REPL, then issue
test(10)it flashes the LED's for 10 seconds. If you repeattest(10)it runs for 10 seconds but no LED's flash.From the code here: https://github.com/micropython/micropython-lib/blob/master/uasyncio.core/uasyncio/core.py#L162
_event_loopis a global in the module, andget_event_loop()returns it the next time we call it from an already imported module. I am new to asyncio and uasyncio, however from the examples I have worked with so far, I think this is expected behavior for asyncio -get_event_loop()from any module gets the correct event_loop. It is intuitive when the interpreter quits along with the program.On long-running interpreters, would it make sense to clean up when uasyncio quits? That would give the expected behavior when using REPL, as observed by @peterhinch .
The original issue in the title should be clarified by now. If there're additional issues, please submit them in separate ticket(s) with correspondings titles.