Skip to content

asyncio.run_coroutine_threadsafe drops specified exceptions' traceback #117459

Closed
@rsp4jack

Description

@rsp4jack

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:

def _convert_future_exc(exc):
exc_class = type(exc)
if exc_class is concurrent.futures.CancelledError:
return exceptions.CancelledError(*exc.args)
elif exc_class is concurrent.futures.TimeoutError:
return exceptions.TimeoutError(*exc.args)
elif exc_class is concurrent.futures.InvalidStateError:
return exceptions.InvalidStateError(*exc.args)
else:
return exc

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions