Repository navigation
Implement anext and aiter in pure Python #157361
Description
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
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsTodo
Activity
picnixz commentedon Sep 12, 2026
OOC, how would you do it? do you mean to add some
_builtins.pyfile 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.kumaraditya303 commentedon Sep 12, 2026
See #157362, I am adding the source for
anextas 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>.kumaraditya303 commentedon Sep 12, 2026
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.
deadlovelll commentedon Sep 12, 2026
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 laterpicnixz commentedon Sep 12, 2026
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.
kumaraditya303 commentedon Sep 13, 2026
This will be a new feature, on 3.16+ asyncio can trace through anext and aiter etc as well.
Internal files don't need documentation, we can perhaps add internal docs for core devs but that is not necessary.
deadlovelll commentedon Sep 13, 2026
Thank you for answers! Got it
kumaraditya303 commentedon Sep 16, 2026
With #157362, the JIT is able to inline the calls and has a ~30% speedup with the pure Python implementation.
await anext(it)await anext(it, None)cc @Fidget-Spinner
gh-157361: Implement `anext()` in Python instead of C (#157362)
pythongh-157361: Implement aiter() in Python instead of C
asyncio.print_call_graph()loses the call stack at anext(agen, default) #157032 as a duplicate of this issueasyncio.print_call_graph()loses the call stack ataiter(callable, stop_value)#157044 as a duplicate of this issue