mirror of
https://github.com/python/cpython.git
synced 2025-08-19 08:11:46 +00:00
bpo-45924: Fix asyncio incorrect traceback when future's exception is raised multiple times (GH-30274) (#94748)
(cherry picked from commit 86c1df1872
)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
This commit is contained in:
parent
916686fdb2
commit
aa2142def6
4 changed files with 47 additions and 2 deletions
|
@ -198,7 +198,7 @@ class Future:
|
||||||
raise exceptions.InvalidStateError('Result is not ready.')
|
raise exceptions.InvalidStateError('Result is not ready.')
|
||||||
self.__log_traceback = False
|
self.__log_traceback = False
|
||||||
if self._exception is not None:
|
if self._exception is not None:
|
||||||
raise self._exception
|
raise self._exception.with_traceback(self._exception_tb)
|
||||||
return self._result
|
return self._result
|
||||||
|
|
||||||
def exception(self):
|
def exception(self):
|
||||||
|
@ -274,6 +274,7 @@ class Future:
|
||||||
raise TypeError("StopIteration interacts badly with generators "
|
raise TypeError("StopIteration interacts badly with generators "
|
||||||
"and cannot be raised into a Future")
|
"and cannot be raised into a Future")
|
||||||
self._exception = exception
|
self._exception = exception
|
||||||
|
self._exception_tb = exception.__traceback__
|
||||||
self._state = _FINISHED
|
self._state = _FINISHED
|
||||||
self.__schedule_callbacks()
|
self.__schedule_callbacks()
|
||||||
self.__log_traceback = True
|
self.__log_traceback = True
|
||||||
|
|
|
@ -1,13 +1,42 @@
|
||||||
# IsolatedAsyncioTestCase based tests
|
# IsolatedAsyncioTestCase based tests
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import traceback
|
||||||
import unittest
|
import unittest
|
||||||
|
from asyncio import tasks
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
asyncio.set_event_loop_policy(None)
|
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')
|
||||||
|
|
||||||
|
@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):
|
async def test_recursive_repr_for_pending_tasks(self):
|
||||||
# The call crashes if the guard for recursive call
|
# The call crashes if the guard for recursive call
|
||||||
# in base_futures:_future_repr_info is absent
|
# in base_futures:_future_repr_info is absent
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :mod:`asyncio` incorrect traceback when future's exception is raised multiple times. Patch by Kumar Aditya.
|
|
@ -63,6 +63,7 @@ typedef enum {
|
||||||
PyObject *prefix##_context0; \
|
PyObject *prefix##_context0; \
|
||||||
PyObject *prefix##_callbacks; \
|
PyObject *prefix##_callbacks; \
|
||||||
PyObject *prefix##_exception; \
|
PyObject *prefix##_exception; \
|
||||||
|
PyObject *prefix##_exception_tb; \
|
||||||
PyObject *prefix##_result; \
|
PyObject *prefix##_result; \
|
||||||
PyObject *prefix##_source_tb; \
|
PyObject *prefix##_source_tb; \
|
||||||
PyObject *prefix##_cancel_msg; \
|
PyObject *prefix##_cancel_msg; \
|
||||||
|
@ -487,6 +488,7 @@ future_init(FutureObj *fut, PyObject *loop)
|
||||||
Py_CLEAR(fut->fut_callbacks);
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
Py_CLEAR(fut->fut_result);
|
Py_CLEAR(fut->fut_result);
|
||||||
Py_CLEAR(fut->fut_exception);
|
Py_CLEAR(fut->fut_exception);
|
||||||
|
Py_CLEAR(fut->fut_exception_tb);
|
||||||
Py_CLEAR(fut->fut_source_tb);
|
Py_CLEAR(fut->fut_source_tb);
|
||||||
Py_CLEAR(fut->fut_cancel_msg);
|
Py_CLEAR(fut->fut_cancel_msg);
|
||||||
_PyErr_ClearExcState(&fut->fut_cancelled_exc_state);
|
_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);
|
||||||
|
assert(!fut->fut_exception_tb);
|
||||||
fut->fut_exception = exc_val;
|
fut->fut_exception = exc_val;
|
||||||
|
fut->fut_exception_tb = PyException_GetTraceback(exc_val);
|
||||||
fut->fut_state = STATE_FINISHED;
|
fut->fut_state = STATE_FINISHED;
|
||||||
|
|
||||||
if (future_schedule_callbacks(fut) == -1) {
|
if (future_schedule_callbacks(fut) == -1) {
|
||||||
|
@ -641,8 +645,16 @@ future_get_result(FutureObj *fut, PyObject **result)
|
||||||
|
|
||||||
fut->fut_log_tb = 0;
|
fut->fut_log_tb = 0;
|
||||||
if (fut->fut_exception != NULL) {
|
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);
|
Py_INCREF(fut->fut_exception);
|
||||||
*result = fut->fut_exception;
|
*result = fut->fut_exception;
|
||||||
|
Py_CLEAR(fut->fut_exception_tb);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -784,6 +796,7 @@ FutureObj_clear(FutureObj *fut)
|
||||||
Py_CLEAR(fut->fut_callbacks);
|
Py_CLEAR(fut->fut_callbacks);
|
||||||
Py_CLEAR(fut->fut_result);
|
Py_CLEAR(fut->fut_result);
|
||||||
Py_CLEAR(fut->fut_exception);
|
Py_CLEAR(fut->fut_exception);
|
||||||
|
Py_CLEAR(fut->fut_exception_tb);
|
||||||
Py_CLEAR(fut->fut_source_tb);
|
Py_CLEAR(fut->fut_source_tb);
|
||||||
Py_CLEAR(fut->fut_cancel_msg);
|
Py_CLEAR(fut->fut_cancel_msg);
|
||||||
_PyErr_ClearExcState(&fut->fut_cancelled_exc_state);
|
_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_callbacks);
|
||||||
Py_VISIT(fut->fut_result);
|
Py_VISIT(fut->fut_result);
|
||||||
Py_VISIT(fut->fut_exception);
|
Py_VISIT(fut->fut_exception);
|
||||||
|
Py_VISIT(fut->fut_exception_tb);
|
||||||
Py_VISIT(fut->fut_source_tb);
|
Py_VISIT(fut->fut_source_tb);
|
||||||
Py_VISIT(fut->fut_cancel_msg);
|
Py_VISIT(fut->fut_cancel_msg);
|
||||||
Py_VISIT(fut->dict);
|
Py_VISIT(fut->dict);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue