Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 91176d3

Browse filesBrowse files
[3.11] bpo-45924: Fix asyncio incorrect traceback when future's exception is raised multiple times (GH-30274) (#94747)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
1 parent 8464e4a commit 91176d3
Copy full SHA for 91176d3

File tree

Expand file treeCollapse file tree

4 files changed

+47
-2
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+47
-2
lines changed

‎Lib/asyncio/futures.py

Copy file name to clipboardExpand all lines: Lib/asyncio/futures.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def result(self):
206206
raise exceptions.InvalidStateError('Result is not ready.')
207207
self.__log_traceback = False
208208
if self._exception is not None:
209-
raise self._exception
209+
raise self._exception.with_traceback(self._exception_tb)
210210
return self._result
211211

212212
def exception(self):
@@ -282,6 +282,7 @@ def set_exception(self, exception):
282282
raise TypeError("StopIteration interacts badly with generators "
283283
"and cannot be raised into a Future")
284284
self._exception = exception
285+
self._exception_tb = exception.__traceback__
285286
self._state = _FINISHED
286287
self.__schedule_callbacks()
287288
self.__log_traceback = True

‎Lib/test/test_asyncio/test_futures2.py

Copy file name to clipboardExpand all lines: Lib/test/test_asyncio/test_futures2.py
+30-1Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
# IsolatedAsyncioTestCase based tests
22
import asyncio
3+
import traceback
34
import unittest
5+
from asyncio import tasks
46

57

68
def tearDownModule():
79
asyncio.set_event_loop_policy(None)
810

911

10-
class FutureTests(unittest.IsolatedAsyncioTestCase):
12+
class FutureTests:
13+
14+
async def test_future_traceback(self):
15+
16+
async def raise_exc():
17+
raise TypeError(42)
18+
19+
future = self.cls(raise_exc())
20+
21+
for _ in range(5):
22+
try:
23+
await future
24+
except TypeError as e:
25+
tb = ''.join(traceback.format_tb(e.__traceback__))
26+
self.assertEqual(tb.count("await future"), 1)
27+
else:
28+
self.fail('TypeError was not raised')
29+
30+
@unittest.skipUnless(hasattr(tasks, '_CTask'),
31+
'requires the C _asyncio module')
32+
class CFutureTests(FutureTests, unittest.IsolatedAsyncioTestCase):
33+
cls = tasks._CTask
34+
35+
class PyFutureTests(FutureTests, unittest.IsolatedAsyncioTestCase):
36+
cls = tasks._PyTask
37+
38+
class FutureReprTests(unittest.IsolatedAsyncioTestCase):
39+
1140
async def test_recursive_repr_for_pending_tasks(self):
1241
# The call crashes if the guard for recursive call
1342
# in base_futures:_future_repr_info is absent
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :mod:`asyncio` incorrect traceback when future's exception is raised multiple times. Patch by Kumar Aditya.

‎Modules/_asynciomodule.c

Copy file name to clipboardExpand all lines: Modules/_asynciomodule.c
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef enum {
7070
PyObject *prefix##_context0; \
7171
PyObject *prefix##_callbacks; \
7272
PyObject *prefix##_exception; \
73+
PyObject *prefix##_exception_tb; \
7374
PyObject *prefix##_result; \
7475
PyObject *prefix##_source_tb; \
7576
PyObject *prefix##_cancel_msg; \
@@ -495,6 +496,7 @@ future_init(FutureObj *fut, PyObject *loop)
495496
Py_CLEAR(fut->fut_callbacks);
496497
Py_CLEAR(fut->fut_result);
497498
Py_CLEAR(fut->fut_exception);
499+
Py_CLEAR(fut->fut_exception_tb);
498500
Py_CLEAR(fut->fut_source_tb);
499501
Py_CLEAR(fut->fut_cancel_msg);
500502
Py_CLEAR(fut->fut_cancelled_exc);
@@ -601,7 +603,9 @@ future_set_exception(FutureObj *fut, PyObject *exc)
601603
}
602604

603605
assert(!fut->fut_exception);
606+
assert(!fut->fut_exception_tb);
604607
fut->fut_exception = exc_val;
608+
fut->fut_exception_tb = PyException_GetTraceback(exc_val);
605609
fut->fut_state = STATE_FINISHED;
606610

607611
if (future_schedule_callbacks(fut) == -1) {
@@ -656,8 +660,16 @@ future_get_result(FutureObj *fut, PyObject **result)
656660

657661
fut->fut_log_tb = 0;
658662
if (fut->fut_exception != NULL) {
663+
PyObject *tb = fut->fut_exception_tb;
664+
if (tb == NULL) {
665+
tb = Py_None;
666+
}
667+
if (PyException_SetTraceback(fut->fut_exception, tb) < 0) {
668+
return -1;
669+
}
659670
Py_INCREF(fut->fut_exception);
660671
*result = fut->fut_exception;
672+
Py_CLEAR(fut->fut_exception_tb);
661673
return 1;
662674
}
663675

@@ -799,6 +811,7 @@ FutureObj_clear(FutureObj *fut)
799811
Py_CLEAR(fut->fut_callbacks);
800812
Py_CLEAR(fut->fut_result);
801813
Py_CLEAR(fut->fut_exception);
814+
Py_CLEAR(fut->fut_exception_tb);
802815
Py_CLEAR(fut->fut_source_tb);
803816
Py_CLEAR(fut->fut_cancel_msg);
804817
Py_CLEAR(fut->fut_cancelled_exc);
@@ -815,6 +828,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
815828
Py_VISIT(fut->fut_callbacks);
816829
Py_VISIT(fut->fut_result);
817830
Py_VISIT(fut->fut_exception);
831+
Py_VISIT(fut->fut_exception_tb);
818832
Py_VISIT(fut->fut_source_tb);
819833
Py_VISIT(fut->fut_cancel_msg);
820834
Py_VISIT(fut->fut_cancelled_exc);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.