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>