[Python-checkins] bpo-31721: Allow Future._log_traceback to only be set to False (#5009)

Yury Selivanov webhook-mailer at python.org
Mon Dec 25 16:16:13 EST 2017


https://github.com/python/cpython/commit/e0aef4f3cd339a405d2a7fbd35a50afa64834f84
commit: e0aef4f3cd339a405d2a7fbd35a50afa64834f84
branch: master
author: Yury Selivanov <yury at magic.io>
committer: GitHub <noreply at github.com>
date: 2017-12-25T16:16:10-05:00
summary:

bpo-31721: Allow Future._log_traceback to only be set to False (#5009)

files:
A Misc/NEWS.d/next/Library/2017-12-25-11-09-46.bpo-31721.5gM972.rst
M Lib/asyncio/futures.py
M Lib/test/test_asyncio/test_futures.py
M Lib/test/test_asyncio/test_tasks.py
M Modules/_asynciomodule.c

diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 13fb47cdc68..1c05b2231c1 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -65,7 +65,7 @@ class Future:
     #   `yield Future()` (incorrect).
     _asyncio_future_blocking = False
 
-    _log_traceback = False
+    __log_traceback = False
 
     def __init__(self, *, loop=None):
         """Initialize the future.
@@ -90,7 +90,7 @@ def __repr__(self):
                                 ' '.join(self._repr_info()))
 
     def __del__(self):
-        if not self._log_traceback:
+        if not self.__log_traceback:
             # set_exception() was not called, or result() or exception()
             # has consumed the exception
             return
@@ -105,6 +105,16 @@ def __del__(self):
             context['source_traceback'] = self._source_traceback
         self._loop.call_exception_handler(context)
 
+    @property
+    def _log_traceback(self):
+        return self.__log_traceback
+
+    @_log_traceback.setter
+    def _log_traceback(self, val):
+        if bool(val):
+            raise ValueError('_log_traceback can only be set to False')
+        self.__log_traceback = False
+
     def get_loop(self):
         """Return the event loop the Future is bound to."""
         return self._loop
@@ -116,7 +126,7 @@ def cancel(self):
         change the future's state to cancelled, schedule the callbacks and
         return True.
         """
-        self._log_traceback = False
+        self.__log_traceback = False
         if self._state != _PENDING:
             return False
         self._state = _CANCELLED
@@ -162,7 +172,7 @@ def result(self):
             raise CancelledError
         if self._state != _FINISHED:
             raise InvalidStateError('Result is not ready.')
-        self._log_traceback = False
+        self.__log_traceback = False
         if self._exception is not None:
             raise self._exception
         return self._result
@@ -179,7 +189,7 @@ def exception(self):
             raise CancelledError
         if self._state != _FINISHED:
             raise InvalidStateError('Exception is not set.')
-        self._log_traceback = False
+        self.__log_traceback = False
         return self._exception
 
     def add_done_callback(self, fn):
@@ -237,7 +247,7 @@ def set_exception(self, exception):
         self._exception = exception
         self._state = _FINISHED
         self._schedule_callbacks()
-        self._log_traceback = True
+        self.__log_traceback = True
 
     def __await__(self):
         if not self.done():
diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py
index d3396162ba6..ab45ee39ab9 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -374,6 +374,11 @@ def test():
             test()
         fut.cancel()
 
+    def test_log_traceback(self):
+        fut = self._new_future(loop=self.loop)
+        with self.assertRaisesRegex(ValueError, 'can only be set to False'):
+            fut._log_traceback = True
+
     @mock.patch('asyncio.base_events.logger')
     def test_tb_logger_abandoned(self, m_log):
         fut = self._new_future(loop=self.loop)
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 0dc6c32fde4..26e4f643d1a 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -623,6 +623,15 @@ def gen():
         t.cancel()
         self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t)
 
+    def test_log_traceback(self):
+        async def coro():
+            pass
+
+        task = self.new_task(self.loop, coro())
+        with self.assertRaisesRegex(ValueError, 'can only be set to False'):
+            task._log_traceback = True
+        self.loop.run_until_complete(task)
+
     def test_wait_for_timeout_less_then_0_or_0_future_done(self):
         def gen():
             when = yield
diff --git a/Misc/NEWS.d/next/Library/2017-12-25-11-09-46.bpo-31721.5gM972.rst b/Misc/NEWS.d/next/Library/2017-12-25-11-09-46.bpo-31721.5gM972.rst
new file mode 100644
index 00000000000..a989a94a7df
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2017-12-25-11-09-46.bpo-31721.5gM972.rst
@@ -0,0 +1,2 @@
+Prevent Python crash from happening when Future._log_traceback is set to
+True manually.  Now it can only be set to False, or a ValueError is raised.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index f8165abcaa2..5ec4ad182ab 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1058,6 +1058,11 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
     if (is_true < 0) {
         return -1;
     }
+    if (is_true) {
+        PyErr_SetString(PyExc_ValueError,
+                        "_log_traceback can only be set to False");
+        return -1;
+    }
     fut->fut_log_tb = is_true;
     return 0;
 }



More information about the Python-checkins mailing list