So the __aiter__() method should return an object that supports the __anext__() method. According to the Python documentation, the latter of these must either return an awaitable or be a coroutine (async def), but the former is just a regular method.
With this simple example (coded to work in either CPython or MicroPython):
try:
import asyncio
except ImportError:
import uasyncio as asyncio
class SlowCounter:
def __init__(self, n):
self.n = n
def __aiter__(self):
self.i = 0
return self
async def __anext__(self):
await asyncio.sleep(1)
if self.i == self.n:
raise StopAsyncIteration
i = self.i
self.i += 1
return i
async def main():
async for i in SlowCounter(10):
print(i)
asyncio.run(main())
I get the expected behaviour under CPython, but this error on MicroPython:
Traceback (most recent call last):
File "<stdin>", line 29, in <module>
File "uasyncio/core.py", line 1, in run
File "uasyncio/core.py", line 1, in run_until_complete
File "uasyncio/core.py", line 1, in run_until_complete
File "<stdin>", line 26, in main
TypeError: 'SlowCounter' object isn't iterable
Following this example from @peterhinch's V3 tutorial I found that the fix is to make __aiter__() a coroutine, i.e., just make it an async def. However, this seems to be a clear break in behaviour between MicroPython and CPython.
I suspect the problem is this line in the compile_async_for_stmt() function:
compile_await_object_method(comp, MP_QSTR___aiter__);
I'm not familiar with the compiler, but I had a guess that it ought to be just calling the method without awaiting the result and tried changing it to:
EMIT_ARG(load_method, MP_QSTR___aiter__, false);
EMIT_ARG(call_method, 0, 0, 0);
and this works.
I'm not going to put in a PR just yet since this might be subtly wrong and it also has knock-on effects to any code already written that is expecting __aiter()__ to have to return an awaitable / be a co-routine.
So the
__aiter__()method should return an object that supports the__anext__()method. According to the Python documentation, the latter of these must either return an awaitable or be a coroutine (async def), but the former is just a regular method.With this simple example (coded to work in either CPython or MicroPython):
I get the expected behaviour under CPython, but this error on MicroPython:
Following this example from @peterhinch's V3 tutorial I found that the fix is to make
__aiter__()a coroutine, i.e., just make it anasync def. However, this seems to be a clear break in behaviour between MicroPython and CPython.I suspect the problem is this line in the
compile_async_for_stmt()function:I'm not familiar with the compiler, but I had a guess that it ought to be just calling the method without awaiting the result and tried changing it to:
and this works.
I'm not going to put in a PR just yet since this might be subtly wrong and it also has knock-on effects to any code already written that is expecting
__aiter()__to have to return an awaitable / be a co-routine.