Skip to content

In some obscure cases, StopIteration exceptions can be turned into RuntimeErrors when passing through nurseries #229

@njsmith

Description

@njsmith

There are some very obscure edge cases where using async generators does the wrong thing. For example, this code

import trio

async def fail():
    raise ValueError

async def main():
    async with trio.open_nursery() as nursery:
        nursery.spawn(fail)
        raise StopIteration

trio.run(main)

Ought to raise a MultiError([StopIteration(), ValueError()]), but instead it raises a MultiError([RuntimeError(), ValueError()]). This is because you can't throw a StopIteration into an async_generator-based async generator (for details see here) – it gets wrapped into a RuntimeError. If we let that RuntimeError propagate, then acontextmanager could turn it back, but here it gets wrapped into a MultiError instead so the magic doesn't work.

I guess we could make acontextmanager smart enough to look inside MultiErrors? Or stop using acontextmanager and write this context manager directly? Or just not worry about it until we can drop 3.5 support and switch to using native async generators, which don't have this problem? It's very unusual to have free-floating StopIteration exceptions wandering around like this, and that's the only case that's affected. But I figured we should record the problem in any case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions