New GitHub issue #108668 from gschaffner:<br>

<hr>

<pre>
# Bug report

### Checklist

- [X] I am confident this is a bug in CPython, not a bug in a third-party project
- [X] I have searched the [CPython issue tracker](https://github.com/python/cpython/issues?q=is%3Aissue+sort%3Acreated-desc),
and am confident this bug has not been reported before


### CPython versions tested on:

3.8, 3.9, 3.10, 3.11, 3.12, CPython main branch

### Operating systems tested on:

Linux

### Output from running 'python -VV' on the command line:

Python 3.11.5 (main, Aug 28 2023, 16:29:45) [GCC 13.2.1 20230801]

### A clear and concise description of the bug:

if an exception is active inside a coroutine/generator (`coro1`/`agen1`) and another exception is `.throw`n into its `await coro2`/`yield from agen2`, then `coro2`/`agen2` loses the original `exc_info`.

*   a generator example:

    ```python
    import sys


    def f():
        try:
 raise RuntimeError("boom")
        except RuntimeError:  # or `__exit__`/`finally`
            print(sys.exc_info())
            yield from g()
            # note: if you inline `g()` here, things work fine.
 print(sys.exc_info())


    def g():
 print(sys.exc_info())
        try:
            yield
        except ValueError:
            pass
        print(sys.exc_info())


    gen = f()
    gen.send(None)
    try:
        gen.throw(ValueError)
    except StopIteration:
        pass
    ```

    output:

    ```
    (<class 'RuntimeError'>, RuntimeError('boom'), <traceback object at 0x7fdb6b313840>)
    (<class 'RuntimeError'>, RuntimeError('boom'), <traceback object at 0x7fdb6b313840>)
    (None, None, None)
    (<class 'RuntimeError'>, RuntimeError('boom'), <traceback object at 0x7fdb6b313840>)
    ```

    expected output:

    same `exc_info` all four times.

*   two coroutine examples (using asyncio in particular, with asyncio calling `throw`):

    ```python
    import asyncio
    import sys
    from asyncio import CancelledError
    from math import inf


 async def f():
        try:
            raise RuntimeError("boom")
 except RuntimeError:  # or `__aexit__`/`finally`
 print(sys.exc_info())
            await clean_up_and_log()
            # note: if you inline `clean_up_and_log()` here, things work fine.
 print(sys.exc_info())


    async def clean_up_and_log():
 print(sys.exc_info())

        # attempt to do some clean up that we are okay skipping part of if we get cancelled
        # during it or if we already have a cancellation request.
        #
        # (to reproduce, suppose that we get a cancellation request before the optional
        # clean up step.)
        asyncio.current_task().cancel()
        try:
 await asyncio.sleep(0)
        except CancelledError:
            if sys.version_info >= (3, 11):
 asyncio.current_task().uncancel()

        # after the optional async clean up, log the exception.
        print(sys.exc_info())


    async def clean_up_and_log():  # this example is >= 3.11 only.
 print(sys.exc_info())

        # attempt to do some clean up that we want to skip if it takes too long (e.g. due to
        # a non-responsive peer) or if we get cancelled during it or if we already have a
        # cancellation request.
        try:
            async with asyncio.timeout(0.1):
                await asyncio.sleep(inf)
 except TimeoutError:
            pass
        except CancelledError:
 asyncio.current_task().uncancel()

        # after the optional async clean up, log the exception.
        print(sys.exc_info())


 asyncio.run(f())
    ```

    output and expected output: same as the generator example.

this problem is a special case of GH-73773 that was not discussed or fixed as part of that issue[^1].

for comparison, PyPy behaves the way I expect here.

[^1]: I am not sure whether it would be preferred that I necrobump the old closed issue or file a new report here.
</pre>

<hr>

<a href="https://github.com/python/cpython/issues/108668">View on GitHub</a>
<p>Labels: type-bug</p>
<p>Assignee: </p>