Skip to content

Commit 7ce1c6f

Browse files
authored
bpo-30508: Don't log exceptions if Task/Future "cancel()" method called (#2050)
1 parent 36ff451 commit 7ce1c6f

File tree

6 files changed

+49
-1
lines changed

6 files changed

+49
-1
lines changed

Lib/asyncio/futures.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def cancel(self):
107107
change the future's state to cancelled, schedule the callbacks and
108108
return True.
109109
"""
110+
self._log_traceback = False
110111
if self._state != _PENDING:
111112
return False
112113
self._state = _CANCELLED

Lib/asyncio/tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ def cancel(self):
144144
terminates with a CancelledError exception (even if cancel()
145145
was not called).
146146
"""
147+
self._log_traceback = False
147148
if self.done():
148149
return False
149150
if self._fut_waiter is not None:

Lib/test/test_asyncio/test_futures.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,14 @@ def test_tb_logger_abandoned(self, m_log):
318318
del fut
319319
self.assertFalse(m_log.error.called)
320320

321+
@mock.patch('asyncio.base_events.logger')
322+
def test_tb_logger_not_called_after_cancel(self, m_log):
323+
fut = self._new_future(loop=self.loop)
324+
fut.set_exception(Exception())
325+
fut.cancel()
326+
del fut
327+
self.assertFalse(m_log.error.called)
328+
321329
@mock.patch('asyncio.base_events.logger')
322330
def test_tb_logger_result_unretrieved(self, m_log):
323331
fut = self._new_future(loop=self.loop)

Lib/test/test_asyncio/test_tasks.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,25 @@ def kill_me(loop):
18641864
})
18651865
mock_handler.reset_mock()
18661866

1867+
@mock.patch('asyncio.base_events.logger')
1868+
def test_tb_logger_not_called_after_cancel(self, m_log):
1869+
loop = asyncio.new_event_loop()
1870+
self.set_event_loop(loop)
1871+
1872+
@asyncio.coroutine
1873+
def coro():
1874+
raise TypeError
1875+
1876+
@asyncio.coroutine
1877+
def runner():
1878+
task = self.new_task(loop, coro())
1879+
yield from asyncio.sleep(0.05, loop=loop)
1880+
task.cancel()
1881+
task = None
1882+
1883+
loop.run_until_complete(runner())
1884+
self.assertFalse(m_log.error.called)
1885+
18671886
@mock.patch('asyncio.coroutines.logger')
18681887
def test_coroutine_never_yielded(self, m_log):
18691888
with set_coroutine_debug(True):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ Extension Modules
359359
Library
360360
-------
361361

362+
- bpo-30508: Don't log exceptions if Task/Future "cancel()" method was
363+
called.
364+
362365
- bpo-11822: The dis.dis() function now is able to disassemble nested
363366
code objects.
364367

Modules/_asynciomodule.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ future_add_done_callback(FutureObj *fut, PyObject *arg)
306306
static PyObject *
307307
future_cancel(FutureObj *fut)
308308
{
309+
fut->fut_log_tb = 0;
310+
309311
if (fut->fut_state != STATE_PENDING) {
310312
Py_RETURN_FALSE;
311313
}
@@ -639,6 +641,17 @@ FutureObj_get_log_traceback(FutureObj *fut)
639641
}
640642
}
641643

644+
static int
645+
FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
646+
{
647+
int is_true = PyObject_IsTrue(val);
648+
if (is_true < 0) {
649+
return -1;
650+
}
651+
fut->fut_log_tb = is_true;
652+
return 0;
653+
}
654+
642655
static PyObject *
643656
FutureObj_get_loop(FutureObj *fut)
644657
{
@@ -883,7 +896,8 @@ static PyMethodDef FutureType_methods[] = {
883896
{"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL}, \
884897
{"_result", (getter)FutureObj_get_result, NULL, NULL}, \
885898
{"_exception", (getter)FutureObj_get_exception, NULL, NULL}, \
886-
{"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL}, \
899+
{"_log_traceback", (getter)FutureObj_get_log_traceback, \
900+
(setter)FutureObj_set_log_traceback, NULL}, \
887901
{"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL},
888902

889903
static PyGetSetDef FutureType_getsetlist[] = {
@@ -1569,6 +1583,8 @@ static PyObject *
15691583
_asyncio_Task_cancel_impl(TaskObj *self)
15701584
/*[clinic end generated code: output=6bfc0479da9d5757 input=13f9bf496695cb52]*/
15711585
{
1586+
self->task_log_tb = 0;
1587+
15721588
if (self->task_state != STATE_PENDING) {
15731589
Py_RETURN_FALSE;
15741590
}

0 commit comments

Comments
 (0)