[Python-checkins] bpo-45924: Fix asyncio incorrect traceback when future's exception is raised multiple times (GH-30274) (#94748)
pablogsal
webhook-mailer at python.org
Mon Jul 11 10:38:33 EDT 2022
https://github.com/python/cpython/commit/aa2142def669e87016406acf7a84a5254487b0ee
commit: aa2142def669e87016406acf7a84a5254487b0ee
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: pablogsal <Pablogsal at gmail.com>
date: 2022-07-11T15:38:27+01:00
summary:
bpo-45924: Fix asyncio incorrect traceback when future's exception is raised multiple times (GH-30274) (#94748)
(cherry picked from commit 86c1df18727568758cc329baddc1836e45664023)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com>
Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com>
files:
A Misc/NEWS.d/next/Library/2021-12-27-15-32-15.bpo-45924.0ZpHX2.rst
M Lib/asyncio/futures.py
M Lib/test/test_asyncio/test_futures2.py
M Modules/_asynciomodule.c
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 8e8cd87612588..48a32f868385c 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -198,7 +198,7 @@ def result(self):
raise exceptions.InvalidStateError('Result is not ready.')
self.__log_traceback = False
if self._exception is not None:
- raise self._exception
+ raise self._exception.with_traceback(self._exception_tb)
return self._result
def exception(self):
@@ -274,6 +274,7 @@ def set_exception(self, exception):
raise TypeError("StopIteration interacts badly with generators "
"and cannot be raised into a Future")
self._exception = exception
+ self._exception_tb = exception.__traceback__
self._state = _FINISHED
self.__schedule_callbacks()
self.__log_traceback = True
diff --git a/Lib/test/test_asyncio/test_futures2.py b/Lib/test/test_asyncio/test_futures2.py
index 60b58850369f0..71279b69c7929 100644
--- a/Lib/test/test_asyncio/test_futures2.py
+++ b/Lib/test/test_asyncio/test_futures2.py
@@ -1,13 +1,42 @@
# IsolatedAsyncioTestCase based tests
import asyncio
+import traceback
import unittest
+from asyncio import tasks
def tearDownModule():
asyncio.set_event_loop_policy(None)
-class FutureTests(unittest.IsolatedAsyncioTestCase):
+class FutureTests:
+
+ async def test_future_traceback(self):
+
+ async def raise_exc():
+ raise TypeError(42)
+
+ future = self.cls(raise_exc())
+
+ for _ in range(5):
+ try:
+ await future
+ except TypeError as e:
+ tb = ''.join(traceback.format_tb(e.__traceback__))
+ self.assertEqual(tb.count("await future"), 1)
+ else:
+ self.fail('TypeError was not raised')
+
+ at unittest.skipUnless(hasattr(tasks, '_CTask'),
+ 'requires the C _asyncio module')
+class CFutureTests(FutureTests, unittest.IsolatedAsyncioTestCase):
+ cls = tasks._CTask
+
+class PyFutureTests(FutureTests, unittest.IsolatedAsyncioTestCase):
+ cls = tasks._PyTask
+
+class FutureReprTests(unittest.IsolatedAsyncioTestCase):
+
async def test_recursive_repr_for_pending_tasks(self):
# The call crashes if the guard for recursive call
# in base_futures:_future_repr_info is absent
diff --git a/Misc/NEWS.d/next/Library/2021-12-27-15-32-15.bpo-45924.0ZpHX2.rst b/Misc/NEWS.d/next/Library/2021-12-27-15-32-15.bpo-45924.0ZpHX2.rst
new file mode 100644
index 0000000000000..5cda22737adb7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-12-27-15-32-15.bpo-45924.0ZpHX2.rst
@@ -0,0 +1 @@
+Fix :mod:`asyncio` incorrect traceback when future's exception is raised multiple times. Patch by Kumar Aditya.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 392e0e7c7357f..1f6de2177a37a 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -63,6 +63,7 @@ typedef enum {
PyObject *prefix##_context0; \
PyObject *prefix##_callbacks; \
PyObject *prefix##_exception; \
+ PyObject *prefix##_exception_tb; \
PyObject *prefix##_result; \
PyObject *prefix##_source_tb; \
PyObject *prefix##_cancel_msg; \
@@ -487,6 +488,7 @@ future_init(FutureObj *fut, PyObject *loop)
Py_CLEAR(fut->fut_callbacks);
Py_CLEAR(fut->fut_result);
Py_CLEAR(fut->fut_exception);
+ Py_CLEAR(fut->fut_exception_tb);
Py_CLEAR(fut->fut_source_tb);
Py_CLEAR(fut->fut_cancel_msg);
_PyErr_ClearExcState(&fut->fut_cancelled_exc_state);
@@ -593,7 +595,9 @@ future_set_exception(FutureObj *fut, PyObject *exc)
}
assert(!fut->fut_exception);
+ assert(!fut->fut_exception_tb);
fut->fut_exception = exc_val;
+ fut->fut_exception_tb = PyException_GetTraceback(exc_val);
fut->fut_state = STATE_FINISHED;
if (future_schedule_callbacks(fut) == -1) {
@@ -641,8 +645,16 @@ future_get_result(FutureObj *fut, PyObject **result)
fut->fut_log_tb = 0;
if (fut->fut_exception != NULL) {
+ PyObject *tb = fut->fut_exception_tb;
+ if (tb == NULL) {
+ tb = Py_None;
+ }
+ if (PyException_SetTraceback(fut->fut_exception, tb) < 0) {
+ return -1;
+ }
Py_INCREF(fut->fut_exception);
*result = fut->fut_exception;
+ Py_CLEAR(fut->fut_exception_tb);
return 1;
}
@@ -784,6 +796,7 @@ FutureObj_clear(FutureObj *fut)
Py_CLEAR(fut->fut_callbacks);
Py_CLEAR(fut->fut_result);
Py_CLEAR(fut->fut_exception);
+ Py_CLEAR(fut->fut_exception_tb);
Py_CLEAR(fut->fut_source_tb);
Py_CLEAR(fut->fut_cancel_msg);
_PyErr_ClearExcState(&fut->fut_cancelled_exc_state);
@@ -800,6 +813,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
Py_VISIT(fut->fut_callbacks);
Py_VISIT(fut->fut_result);
Py_VISIT(fut->fut_exception);
+ Py_VISIT(fut->fut_exception_tb);
Py_VISIT(fut->fut_source_tb);
Py_VISIT(fut->fut_cancel_msg);
Py_VISIT(fut->dict);
More information about the Python-checkins
mailing list