]> granicus.if.org Git - python/commitdiff
bpo-30508: Don't log exceptions if Task/Future "cancel()" method was called. (#2109)
authorYury Selivanov <yury@magic.io>
Sun, 11 Jun 2017 14:00:14 +0000 (14:00 +0000)
committerGitHub <noreply@github.com>
Sun, 11 Jun 2017 14:00:14 +0000 (14:00 +0000)
Lib/asyncio/futures.py
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_futures.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS
Modules/_asynciomodule.c

index d11d289307eaf341a9ce411f6ae6c5e06b79d454..cff9590e4ead5f3f5dd755891b8a979faf0034a6 100644 (file)
@@ -191,6 +191,7 @@ class Future:
         change the future's state to cancelled, schedule the callbacks and
         return True.
         """
+        self._log_traceback = False
         if self._state != _PENDING:
             return False
         self._state = _CANCELLED
index d7867d128a8afe97fe7edcb63c5a8b609b04070b..9fe2a2fabf076a351d41044569e8057e5b4c32b3 100644 (file)
@@ -148,6 +148,7 @@ class Task(futures.Future):
         terminates with a CancelledError exception (even if cancel()
         was not called).
         """
+        self._log_traceback = False
         if self.done():
             return False
         if self._fut_waiter is not None:
index 99336f86ab824ebe80cde2fd90587136e0bdda44..5d4b2d2aa0efc0b73ec266389a5823dab91c820d 100644 (file)
@@ -318,6 +318,14 @@ class BaseFutureTests:
         del fut
         self.assertFalse(m_log.error.called)
 
+    @mock.patch('asyncio.base_events.logger')
+    def test_tb_logger_not_called_after_cancel(self, m_log):
+        fut = self._new_future(loop=self.loop)
+        fut.set_exception(Exception())
+        fut.cancel()
+        del fut
+        self.assertFalse(m_log.error.called)
+
     @mock.patch('asyncio.base_events.logger')
     def test_tb_logger_result_unretrieved(self, m_log):
         fut = self._new_future(loop=self.loop)
index 5462c80ad3013a9a5aa9dd570f9d34f7a2a63895..195a1ed40770ddaf94eb7584a61fd031cb5b3113 100644 (file)
@@ -1865,6 +1865,25 @@ class BaseTaskTests:
         })
         mock_handler.reset_mock()
 
+    @mock.patch('asyncio.base_events.logger')
+    def test_tb_logger_not_called_after_cancel(self, m_log):
+        loop = asyncio.new_event_loop()
+        self.set_event_loop(loop)
+
+        @asyncio.coroutine
+        def coro():
+            raise TypeError
+
+        @asyncio.coroutine
+        def runner():
+            task = self.new_task(loop, coro())
+            yield from asyncio.sleep(0.05, loop=loop)
+            task.cancel()
+            task = None
+
+        loop.run_until_complete(runner())
+        self.assertFalse(m_log.error.called)
+
     @mock.patch('asyncio.coroutines.logger')
     def test_coroutine_never_yielded(self, m_log):
         with set_coroutine_debug(True):
index d31ae7ee9b6ffe3fe9566a0fe2f288618267fa0b..5f44a790f7f9f20c68b6eb747fcc984acb77b083 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,9 @@ Core and Builtins
 Library
 -------
 
+- bpo-30508: Don't log exceptions if Task/Future "cancel()" method was
+  called.
+
 - bpo-28556: Updates to typing module: Add generic AsyncContextManager, add
   support for ContextManager on all versions. Original PRs by Jelle Zijlstra
   and Ivan Levkivskyi
index 75327fa3014be95a129d4cd11fd7b62766fd7c6f..492b98359713df2ace13221e77a718b0eacfb995 100644 (file)
@@ -305,6 +305,8 @@ future_add_done_callback(FutureObj *fut, PyObject *arg)
 static PyObject *
 future_cancel(FutureObj *fut)
 {
+    fut->fut_log_tb = 0;
+
     if (fut->fut_state != STATE_PENDING) {
         Py_RETURN_FALSE;
     }
@@ -638,6 +640,17 @@ FutureObj_get_log_traceback(FutureObj *fut)
     }
 }
 
+static int
+FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
+{
+    int is_true = PyObject_IsTrue(val);
+    if (is_true < 0) {
+        return -1;
+    }
+    fut->fut_log_tb = is_true;
+    return 0;
+}
+
 static PyObject *
 FutureObj_get_loop(FutureObj *fut)
 {
@@ -882,7 +895,8 @@ static PyMethodDef FutureType_methods[] = {
     {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL},              \
     {"_result", (getter)FutureObj_get_result, NULL, NULL},                    \
     {"_exception", (getter)FutureObj_get_exception, NULL, NULL},              \
-    {"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL},      \
+    {"_log_traceback", (getter)FutureObj_get_log_traceback,                   \
+                       (setter)FutureObj_set_log_traceback, NULL},            \
     {"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL},
 
 static PyGetSetDef FutureType_getsetlist[] = {
@@ -1568,6 +1582,8 @@ static PyObject *
 _asyncio_Task_cancel_impl(TaskObj *self)
 /*[clinic end generated code: output=6bfc0479da9d5757 input=13f9bf496695cb52]*/
 {
+    self->task_log_tb = 0;
+
     if (self->task_state != STATE_PENDING) {
         Py_RETURN_FALSE;
     }