GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-155126: Implement PEP 828 by ZeroIntensity · Pull Request #155127 · python/cpython Conversation
|
Documentation build overview
423 files changed ·
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
cc @1st1 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Co-authored-by: Bartosz Sławecki <[email protected]>
There was an error while loading. Please reload this page.
…/implementation
Treat a deleted `StopAsyncIteration.value` as `None` when handling delegated iterator exhaustion. Add a regression test covering immediate exhaustion and exhaustion after yielding an item.
| // this was called from athrow(). This broke some tests/expected behavior | ||
| // for yield from in asyncgens. Removing the parameter didn't seem to cause | ||
| // any new test failures, nor could I reproduce any different behavior | ||
| // when experimenting with it, but we need to be careful. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Neither configuration is correct. Removing the flag breaks asynchronous cleanup; restoring it fixes that case but breaks the required close semantics for GeneratorExit.
I rebuilt and tested both configurations, restoring the original flag plumbing from main: gen_throw() passes 1, the initial aclose()/athrow() calls pass 0, and recursive calls preserve the flag.
| Case | Current branch, flag removed | Flag restored |
|---|---|---|
| Delegate’s cleanup completes synchronously | Works | Works |
| Delegate’s cleanup suspends during aclose() | Fails with RuntimeError; cleanup incomplete | Works |
| Delegate’s cleanup suspends during athrow(GeneratorExit) | Same failure | Works |
| Delegate suppresses GeneratorExit during outer athrow(GeneratorExit) | Correctly reraises the outer exception | Incorrectly allows delegation to finish normally |
| Delegate yields an item while handling GeneratorExit | Correctly raises RuntimeError | Outer athrow() incorrectly returns that item |
| Delegate raises its own GeneratorExit during closing | Preserves expected outer exception identity | Identity test fails |
| Ordinary async generator’s own cleanup awaits | Works | Works |
| Custom async iterator implements aclose()/athrow() | Methods skipped | Methods skipped |
The missing case in the tests is cleanup that actually suspends. This reproduces the current branch’s problem:
import asyncio async def inner(): try: yield 1 finally: await asyncio.sleep(0) print("cleanup completed") async def outer(sub): yield from sub async def main(): sub = inner() gen = outer(sub) await anext(gen) await gen.aclose() asyncio.run(main())
Currently, aclose() raises:
RuntimeError: async generator ignored GeneratorExit
The cleanup never reaches the print. With the flag restored, cleanup completes and aclose() succeeds.
The cause is gen_close_iter() (Objects/genobject.c:427): the PR adds async generators to the branch that calls synchronous gen_close(). When cleanup suspends at an await, gen_close() treats the suspension as an illegal yield and raises RuntimeError.
Restoring the flag only exchanges one problem for another. It makes _gen_throw() forward GeneratorExit into the delegate. But async delegation must await the delegate’s aclose() and then reraise the original exception. Forwarding through throw lets the delegate suppress the exception or yield another item. That distinction is explicit in PEP 828’s reference expansion.
The custom iterator failures expose the same underlying mismatch: the current helpers look for synchronous .close() and .throw(), rather than calling and awaiting .aclose() and .athrow(). I confirmed this against a Python implementation of the PEP’s expansion; the reference calls those methods, while native delegation skips them in both configurations.
The fix needs to distinguish closing an async delegate from throwing into it, and provide a resumable path for awaiting its aclose(). A boolean change alone cannot provide both behaviors. I would investigate handling this in the async delegation bytecode expansion, where the operation can suspend, rather than extending synchronous gen_close_iter().
Validation:
The source and interpreter are restored to your current implementation; the working tree is clean. The comparison probe (/private/tmp/pep828-protocol-probe.py) and delegation edge-case probe (/private/tmp/pep828-delegation-probe.py) remain available for review.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
🤖 New build scheduled with the buildbot fleet by @ZeroIntensity for commit 6daaafc 🤖 Results will be shown at: https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F155127%2Fmerge If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.