Skip to content

Implement anext and aiter in pure Python #157361

Open

Description

@kumaraditya303

Feature or enhancement

Feature or enhancement

I propose to implement anext() and aiter() in Python instead of C, compiled at interpreter startup and copied into builtins. This removes the private anext_awaitable, async_callable_iterator and async_callable_iterator_awaitable types.

anext(it, default) becomes a plain coroutine that awaits type(it).__anext__(it) and converts StopAsyncIteration into the default. The callable form of aiter() (gh-64862) becomes a small class with async def __anext__ that calls and awaits the callable and turns the sentinel or stop_exception into StopAsyncIteration. Semantics stay the same.

The motivation is #157032 and #157044: the C awaitables break the asyncio await graph. capture_call_graph walks cr_await/ag_await, and these types have neither, so the graph stops at the frame calling anext(it, default) or iterating aiter(callable, sentinel) and hides where the task is actually suspended. This would fix #157032, #157044 without any special casing.

Repro with a class-based async iterator:

import asyncio

async def fetch():
    await asyncio.sleep(10)

class AIter:
    def __aiter__(self): return self
    async def __anext__(self):
        await fetch()
        return 1

async def consumer():
    await anext(AIter(), None)

async def main():
    t = asyncio.create_task(consumer(), name="consumer")
    for _ in range(3):
        await asyncio.sleep(0)
    asyncio.print_call_graph(t)
    t.cancel()

asyncio.run(main())

Before:

* Task(name='consumer', id=0x10a42c7d0)
  + Call stack:
  |   File 'demo.py', line 13, in async consumer()

After:

* Task(name='consumer', id=0x108959b80)
  + Call stack:
  |   File 'Lib/asyncio/tasks.py', line 707, in async sleep()
  |   File 'demo.py', line 4, in async fetch()
  |   File 'demo.py', line 9, in async AIter.__anext__()
  |   File '<builtins>', line 26, in async _anext_with_default()
  |   File 'demo.py', line 13, in async consumer()

Linked PRs

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Sep 12, 2026
  2. picnixz commented on Sep 12, 2026

    @picnixz
    Member

    I propose to implement anext() and aiter() in Python instead of C, compiled at interpreter startup and copied into builtins

    OOC, how would you do it? do you mean to add some _builtins.py file somewhere or do you mean that we do it differently? I never had to deal with this specific problem but it would be good to know whether we have the architecture in place to avoid future issues.

  3. kumaraditya303 commented on Sep 12, 2026

    @kumaraditya303
    ContributorAuthor

    OOC, how would you do it? do you mean to add some _builtins.py file somewhere or do you mean that we do it differently? I never had to deal with this specific problem but it would be good to know whether we have the architecture in place to avoid future issues.

    See #157362, I am adding the source for anext as a C string and compiling it at startup and adding it to builtins dict, doing this in C is easier as I can compile function manually with filename as <builtins>.

  4. kumaraditya303 commented on Sep 12, 2026

    @kumaraditya303
    ContributorAuthor

    We do have module freezing infrastructure inplace so we can use that as well. I'll try that as well and if it works for builtins as well then might as well reuse that for this.

  5. added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    and removed
    stdlibStandard Library Python modules in the Lib/ directory
    on Sep 12, 2026
  6. deadlovelll commented on Sep 12, 2026

    @deadlovelll
    Contributor

    it looks good! This is more of a solution to the problem than to the symptom

    This would go to 3.16, i assume this wouldn't be backported to 3.14/3.15, will there be a minimal fix for previous versions or are we accepting the fact that the graph will have a bug on older versions?

    And for _builtins.py - could it be documented what qualifies for this file? I saw in the pr docsting of this file and it documents what it does but not what belongs there, maybe it would be easy to misunderstand later

  7. picnixz commented on Sep 12, 2026

    @picnixz
    Member

    could it be documented what qualifies for this file

    I don't think so. It's internal and others are not meant to use it. It's for CPython only not for the outside world and core developers would know what to put there if we keep the file.

  8. kumaraditya303 commented on Sep 13, 2026

    @kumaraditya303
    ContributorAuthor

    i assume this wouldn't be backported to 3.14/3.15, will there be a minimal fix for previous versions or are we accepting the fact that the graph will have a bug on older versions?

    This will be a new feature, on 3.16+ asyncio can trace through anext and aiter etc as well.

    And for _builtins.py - could it be documented what qualifies for this file? I saw in the pr docsting of this file and it documents what it does but not what belongs there, maybe it would be easy to misunderstand later

    Internal files don't need documentation, we can perhaps add internal docs for core devs but that is not necessary.

  9. deadlovelll commented on Sep 13, 2026

    @deadlovelll
    Contributor

    Thank you for answers! Got it

  10. kumaraditya303 commented on Sep 16, 2026

    @kumaraditya303
    ContributorAuthor

    With #157362, the JIT is able to inline the calls and has a ~30% speedup with the pure Python implementation.

    no JIT JIT speedup
    await anext(it) 98.5 ns 76.7 ns 1.28×
    await anext(it, None) 148.3 ns 112.0 ns 1.32×

    cc @Fidget-Spinner

  11. added 2 commits that reference this issue on Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.16new features, bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)topic-asynciotype-featureA feature request or enhancement

Projects

  • Status
    Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions