Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-45924: Fix asyncio incorrect traceback when future's exception is raised multiple times #30274

Merged
merged 32 commits into from
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
15a9cfe
bpo-45924: Fix asyncio incorrect traceback when future's exception is…
kumaraditya303 Dec 27, 2021
4e5accf
Update Lib/test/test_asyncio/test_futures2.py
asvetlov Feb 15, 2022
42600eb
Update test_futures2.py
asvetlov Feb 15, 2022
d792cc4
Fix test
asvetlov Feb 15, 2022
858fcaf
Fix altered execution error
asvetlov Feb 15, 2022
c192f03
Fix line numbers
asvetlov Feb 15, 2022
72e23ef
Make test more robust
asvetlov Feb 15, 2022
94d0ef9
Merge branch 'main' into issue45924
asvetlov Feb 15, 2022
10b1125
Fix docs
asvetlov Feb 15, 2022
02955ae
Cleanup
asvetlov Feb 15, 2022
842c78f
Merge branch 'main' into issue45924
asvetlov Feb 15, 2022
f501ce1
Merge branch 'main' of https://github.com/python/cpython into issue45924
kumaraditya303 Feb 26, 2022
3a81ce8
fix it with IsolatedAsyncioTestCase
kumaraditya303 Feb 28, 2022
7b55533
Merge branch 'main' of https://github.com/python/cpython into issue45924
kumaraditya303 Mar 10, 2022
63b6fc1
revert
kumaraditya303 Mar 21, 2022
d04e79c
Merge branch 'main' of https://github.com/python/cpython into issue45924
kumaraditya303 Mar 21, 2022
e437cd8
Update Modules/_asynciomodule.c
kumaraditya303 Mar 21, 2022
3312f53
Merge branch 'main' into issue45924
kumaraditya303 Apr 17, 2022
3a671d2
Merge branch 'main' into issue45924
kumaraditya303 Apr 19, 2022
9c9f0e6
Merge branch 'main' into issue45924
kumaraditya303 Apr 28, 2022
9c8577c
Merge branch 'main' of https://github.com/python/cpython into issue45924
kumaraditya303 May 4, 2022
79c8929
Merge branch 'main' into issue45924
kumaraditya303 May 27, 2022
827d57b
Merge branch 'main' into issue45924
kumaraditya303 Jun 30, 2022
07f5000
finally working!
kumaraditya303 Jun 30, 2022
a433abc
rename var
kumaraditya303 Jul 1, 2022
004f64e
Merge branch 'main' into issue45924
kumaraditya303 Jul 6, 2022
9d6bae0
test both implementations
kumaraditya303 Jul 9, 2022
998f33f
separate tests
kumaraditya303 Jul 10, 2022
bf757ce
Update Lib/test/test_asyncio/test_futures2.py
kumaraditya303 Jul 11, 2022
0eb9594
Update Lib/test/test_asyncio/test_futures2.py
kumaraditya303 Jul 11, 2022
c9a6fe7
fix indent
kumaraditya303 Jul 11, 2022
88b819b
Merge branch 'main' into issue45924
iritkatriel Jul 11, 2022
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 @@ -206,7 +206,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 @@ -282,6 +282,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
28 changes: 27 additions & 1 deletion Lib/test/test_asyncio/test_futures2.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
# 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:

kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved
async def test_future_traceback(self):
kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved

async def raise_exc():
raise TypeError(42)

future = self.cls(raise_exc())

for _ in range(5):
kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved
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')

kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved
async def test_recursive_repr_for_pending_tasks(self):
kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved
# The call crashes if the guard for recursive call
# in base_futures:_future_repr_info is absent
Expand All @@ -21,6 +40,13 @@ async def func():
# exact comparison for the whole string is even weaker.
self.assertIn('...', repr(await asyncio.wait_for(func(), timeout=10)))

@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
kumaraditya303 marked this conversation as resolved.
Show resolved Hide resolved

if __name__ == '__main__':
unittest.main()
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 @@ -70,6 +70,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 @@ -495,6 +496,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);
Py_CLEAR(fut->fut_cancelled_exc);
Expand Down Expand Up @@ -601,7 +603,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 @@ -656,8 +660,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 @@ -799,6 +811,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);
Py_CLEAR(fut->fut_cancelled_exc);
Expand All @@ -815,6 +828,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->fut_cancelled_exc);
Expand Down