From 7bc2f07a3a3e65beb73573695bf94817f2a80d16 Mon Sep 17 00:00:00 2001 From: rsp4jack Date: Thu, 4 Apr 2024 11:13:32 +0800 Subject: [PATCH] gh-117459: Keep the traceback in _convert_future_exc (#117460) --- Lib/asyncio/futures.py | 6 ++---- Lib/test/test_asyncio/test_futures.py | 19 +++++++++++++++++++ ...-04-02-13-13-46.gh-issue-117459.jiIZmH.rst | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-04-02-13-13-46.gh-issue-117459.jiIZmH.rst diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 5d35321db7943b0..9c1b5e49e1a70be 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -319,11 +319,9 @@ def _set_result_unless_cancelled(fut, result): 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) + return exceptions.CancelledError(*exc.args).with_traceback(exc.__traceback__) elif exc_class is concurrent.futures.InvalidStateError: - return exceptions.InvalidStateError(*exc.args) + return exceptions.InvalidStateError(*exc.args).with_traceback(exc.__traceback__) else: return exc diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index d3e8efec1c04c2b..458b70451a306a8 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -5,6 +5,7 @@ import re import sys import threading +import traceback import unittest from unittest import mock from types import GenericAlias @@ -416,6 +417,24 @@ def test_copy_state(self): _copy_future_state(f_cancelled, newf_cancelled) self.assertTrue(newf_cancelled.cancelled()) + try: + raise concurrent.futures.InvalidStateError + except BaseException as e: + f_exc = e + + f_conexc = self._new_future(loop=self.loop) + f_conexc.set_exception(f_exc) + + newf_conexc = self._new_future(loop=self.loop) + _copy_future_state(f_conexc, newf_conexc) + self.assertTrue(newf_conexc.done()) + try: + newf_conexc.result() + except BaseException as e: + newf_exc = e # assertRaises context manager drops the traceback + newf_tb = ''.join(traceback.format_tb(newf_exc.__traceback__)) + self.assertEqual(newf_tb.count('raise concurrent.futures.InvalidStateError'), 1) + def test_iter(self): fut = self._new_future(loop=self.loop) diff --git a/Misc/NEWS.d/next/Library/2024-04-02-13-13-46.gh-issue-117459.jiIZmH.rst b/Misc/NEWS.d/next/Library/2024-04-02-13-13-46.gh-issue-117459.jiIZmH.rst new file mode 100644 index 000000000000000..549bd44112befe1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-04-02-13-13-46.gh-issue-117459.jiIZmH.rst @@ -0,0 +1 @@ +:meth:`asyncio.asyncio.run_coroutine_threadsafe` now keeps the traceback of :class:`CancelledError`, :class:`TimeoutError` and :class:`InvalidStateError` which are raised in the coroutine.