Skip to content

py/compile: __aiter__ special method shouldn't be async #6267

Description

@jonathanhogg

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    py-coreRelates to py/ directory in source

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions