python / cpython

The Python programming language
https://www.python.org
Other
63.41k stars 30.37k forks source link

asyncio.run_coroutine_threadsafe drops specified exceptions' traceback #117459

Closed rsp4jack closed 7 months ago

rsp4jack commented 7 months ago

Bug report

Bug description:

import asyncio
import threading
import traceback

async def raiseme():
    raise ValueError(42)
async def raiseme2():
    raise asyncio.TimeoutError()

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
thr = threading.Thread(target=loop.run_forever, daemon=True)
thr.start()

print('raiseme() run_coroutine_threadsafe')
try:
    task = asyncio.run_coroutine_threadsafe(raiseme(), loop)
    task.result()
except:
    traceback.print_exc()

print('raiseme2() run_coroutine_threadsafe')
try:
    task = asyncio.run_coroutine_threadsafe(raiseme2(), loop)
    task.result()
except:
    traceback.print_exc()
raiseme() run_coroutine_threadsafe
Traceback (most recent call last):
  File "g:\Projects\NowPlaying\test.py", line 18, in <module>
    task.result()
  File "C:\Program Files\Python312\Lib\concurrent\futures\_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python312\Lib\concurrent\futures\_base.py", line 401, in __get_result
    raise self._exception
  File "g:\Projects\NowPlaying\test.py", line 6, in raiseme
    raise ValueError(42)
ValueError: 42
raiseme2() run_coroutine_threadsafe
Traceback (most recent call last):
  File "g:\Projects\NowPlaying\test.py", line 25, in <module>
    task.result()
  File "C:\Program Files\Python312\Lib\concurrent\futures\_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python312\Lib\concurrent\futures\_base.py", line 401, in __get_result
    raise self._exception
TimeoutError

The traceback of the second exception (TimeoutError) is dropeed.

The reason is that _convert_future_exc drops the origin exception's traceback: https://github.com/python/cpython/blob/812245ecce2d8344c3748228047bab456816180a/Lib/asyncio/futures.py#L316-L325

To fix it, construct the new exception with the original traceback like that:

return exceptions.CancelledError(*exc.args).with_traceback(exc.__traceback__)

CPython versions tested on:

3.10, CPython main branch

Operating systems tested on:

Linux, Windows

Linked PRs

graingert commented 7 months ago

The check for concurrent.futures.TimeoutError could be removed now that it's the same type as asyncio.TimeoutError

rsp4jack commented 7 months ago

@graingert Thanks, removed in 214f7934ae4db7e7dd32e76a038a4332fa59b8bf