Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Lib/asyncio/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
31 changes: 30 additions & 1 deletion Lib/test/test_asyncio/test_futures2.py
Original file line number Diff line number Diff line change
@@ -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')

@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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix :mod:`asyncio` incorrect traceback when future's exception is raised multiple times. Patch by Kumar Aditya.
14 changes: 14 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; \
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down