Skip to content

Conversation

@YashSuthar983
Copy link
Contributor

@YashSuthar983 YashSuthar983 commented Oct 28, 2025

issue : #3609

Currently passes

import builtins
import asyncio

async def agen():
    for i in range(3):
        yield i

async def main():
    aiter = agen()
    val1 = await builtins.anext(aiter)
    print("val1:", val1)
    val2 = await builtins.anext(aiter)
    print("val2:", val2)

    val3 = await builtins.anext(aiter, "done")
    print("val3:", val3)

asyncio.run(main())

Failed case ,need PyAnextAwaitable

import asyncio

async def simple_test():
    async def agen():
        yield 1
    
    aiter = agen()
    print(f"First value: {await anext(aiter)}")
    
    # EXPECTED: Should return "no more values"
    result = await anext(aiter, "no more values")
    print(f"Second value: {result}")

if __name__ == "__main__":
    asyncio.run(simple_test())
#Cpython 
First value: 1
Second value: no more values

#RustPython

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/rustpython /home/yash/test_anext_simple.py`
First value: 1
Traceback (most recent call last):
  File "/home/yash/test_anext_simple.py", line 22, in <module>
    asyncio.run(simple_test())
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/runners.py", line 193, in run
    with Runner(debug=debug, loop_factory=loop_factory) as runner:
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/runners.py", line 194, in run
    return runner.run(main)
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/runners.py", line 129, in run
    signal.signal(signal.SIGINT, signal.default_int_handler)
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/runners.py", line 124, in run
    raise  # CancelledError
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/base_events.py", line 687, in run_until_complete
    return future.result()
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/futures.py", line 203, in result
    raise self._exception.with_traceback(self._exception_tb)
  File "/home/yash/contri/RustPython/pylib/Lib/asyncio/tasks.py", line 314, in __step_run_and_handle_result
    result = coro.send(None)
  File "/home/yash/test_anext_simple.py", line 18, in simple_test
    result = await anext(aiter, "no more values")
StopAsyncIteration

Summary by CodeRabbit

  • New Features
    • Added anext builtin function to retrieve the next item from asynchronous iterators with optional default value support.

issue : RustPython#3609

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (2)
  • Lib/test/test_asyncgen.py is excluded by !Lib/**
  • Lib/test/test_contextlib_async.py is excluded by !Lib/**

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

A new anext builtin function is added to the RustPython virtual machine's builtins module. The function accepts an asynchronous iterator and an optional default value, calling the iterator's __anext__ method to retrieve an awaitable. Default value handling is incomplete and marked with a TODO comment.

Changes

Cohort / File(s) Summary
Async Iterator Advancement
vm/src/stdlib/builtins.rs
Added anext() builtin function that invokes __anext__ on an async iterator to obtain an awaitable; default value parameter is accepted but not yet fully implemented

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant Builtin as anext() Builtin
    participant Iterator as Async Iterator
    participant Awaitable as __anext__ Awaitable

    User->>Builtin: anext(aiter, [default_value])
    Builtin->>Iterator: Access __anext__ method
    Iterator->>Awaitable: Return awaitable
    Awaitable-->>Builtin: Awaitable object
    alt default_value provided
        Note over Builtin: TODO: Handle default value<br/>(incomplete)
    end
    Builtin-->>User: Return awaitable
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Single new function with straightforward logic flow
  • Incomplete default value handling marked as TODO—clarify intended behavior with PyAnextAwaitable wrapper
  • Verify proper integration with existing __anext__ protocol and async iterator infrastructure

Poem

🐰 A rabbit hops through async streams so fine,
With anext() now in place—the functions align!
Though defaults await their complete design,
Await-ables flow where iterators shine. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Implement minimal builtins.anext()" directly and accurately describes the main change in the pull request, which is the addition of a new anext builtin function to the builtins module. The title is concise, clear, and uses the word "minimal" to appropriately reflect that the implementation is incomplete (specifically lacking full default_value handling as noted in the PR objectives). The title is specific enough that a teammate reviewing the git history would immediately understand that a new builtin function was added, rather than other types of changes like bug fixes or refactorings.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@YashSuthar983
Copy link
Contributor Author

@youknowone can you provide some direction to implement PyAnextAwaitable.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fda9cee and d08b04e.

📒 Files selected for processing (1)
  • vm/src/stdlib/builtins.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Format Rust code with the default rustfmt style (run cargo fmt)
Run clippy and fix any warnings or lints introduced by your changes
Follow Rust best practices for error handling and memory management

Files:

  • vm/src/stdlib/builtins.rs
{vm,stdlib}/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use RustPython macros (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • vm/src/stdlib/builtins.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Check Rust code with rustfmt and clippy
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Ensure compilation on various targets

Comment on lines +537 to +551
#[pyfunction]
fn anext(
aiter: PyObjectRef,
default_value: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let awaitable = vm.call_method(&aiter, "__anext__", ())?;

if default_value.is_missing() {
Ok(awaitable)
} else {
// TODO: Implement CPython like PyAnextAwaitable to properly handle the default value.
Ok(awaitable)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Incomplete default value handling makes the function only partially functional.

The default_value parameter is accepted but completely ignored—both branches return the same awaitable regardless of whether a default is provided. This causes the function to fail the second use case described in the PR objectives: when the async iterator is exhausted and a default value is provided, RustPython raises StopAsyncIteration instead of returning the default like CPython does.

While the TODO comment acknowledges this limitation, having a function signature that accepts a parameter it cannot honor is problematic because:

  • Users expect CPython-compatible behavior
  • The silent acceptance of default_value creates a false sense of functionality
  • It's better to fail fast than fail silently

Consider one of these approaches before merging:

Option 1 (preferred if feasible): Complete the implementation by adding PyAnextAwaitable to wrap the awaitable and handle the default value case.

Option 2: Reject the default parameter explicitly until support is implemented:

 #[pyfunction]
 fn anext(
     aiter: PyObjectRef,
     default_value: OptionalArg<PyObjectRef>,
     vm: &VirtualMachine,
 ) -> PyResult {
     let awaitable = vm.call_method(&aiter, "__anext__", ())?;
 
-    if default_value.is_missing() {
-        Ok(awaitable)
-    } else {
-        // TODO: Implement CPython like PyAnextAwaitable to properly handle the default value.
-        Ok(awaitable)
+    if let OptionalArg::Present(_) = default_value {
+        Err(vm.new_not_implemented_error(
+            "anext() with default value is not yet implemented".to_owned()
+        ))
+    } else {
+        Ok(awaitable)
     }
 }

Option 3: Defer merging this PR until the default value handling is fully implemented.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[pyfunction]
fn anext(
aiter: PyObjectRef,
default_value: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let awaitable = vm.call_method(&aiter, "__anext__", ())?;
if default_value.is_missing() {
Ok(awaitable)
} else {
// TODO: Implement CPython like PyAnextAwaitable to properly handle the default value.
Ok(awaitable)
}
}
#[pyfunction]
fn anext(
aiter: PyObjectRef,
default_value: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let awaitable = vm.call_method(&aiter, "__anext__", ())?;
if let OptionalArg::Present(_) = default_value {
Err(vm.new_not_implemented_error(
"anext() with default value is not yet implemented".to_owned()
))
} else {
Ok(awaitable)
}
}

@youknowone
Copy link
Member

Thanks! By looking the CI result, the test is failing because this patch fixed a few tests:

======================================================================
UNEXPECTED SUCCESS: test_anext_await_raises (test.test_asyncgen.AsyncGenAsyncioTest.test_anext_await_raises)
UNEXPECTED SUCCESS: test_anext_return_generator (test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_generator)
UNEXPECTED SUCCESS: test_anext_return_iterator (test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_iterator)
----------------------------------------------------------------------

Remove @expectedFailure from those tests will fix the CI.

For PyAnextAwaitable, I'd start to defining the type on vm/src/builtins/iter.rs using #[pyclass]. Searching #[pyclass in source code will give some idea about it. Please poke me when you are blocked by something.

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
Copy link
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@youknowone youknowone merged commit 8f048dd into RustPython:main Oct 29, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants