Skip to content

except* can break @trio.as_safe_channel cleanup #3324

@Zac-HD

Description

@Zac-HD

If we break out of a loop over some receive-channel created with @trio.as_safe_channel, the async generator will be cleaned up in the background, and indeed this works:

import trio

async def main():
    async with inner_generator() as recv_chan:
        async for _ in recv_chan:
            break  # Early exit

@trio.as_safe_channel
async def inner_generator():
    try:
        for x in range(10):
            await trio.lowlevel.checkpoint()
            yield x
    except BaseException:  # breaks if `except*`!
        raise
    await trio.lowlevel.checkpoint()

trio.run(main)

...unless you use except*.

Add one little * to the program above, and we instead crash with a BaseExceptionGroup(..., [GeneratorExit()]) - and if you have an outer generator wrapping around the inner one, you'll instead see our BaseExceptionGroup: Encountered exception during cleanup of generator object, as well as exception in the contextmanager body - unable to unwrap error.

Inserting if eg.subgroup(GeneratorExit): raise GeneratorExit from eg before the inner try: block here fixes this issue and I think is the right approach, but I'd love to hear your takes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions