aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2017-12-25 16:16:10 -0500
committerGitHub <noreply@github.com>2017-12-25 16:16:10 -0500
commite0aef4f3cd339a405d2a7fbd35a50afa64834f84 (patch)
tree3d141ffc779ca6159b2e193d95ef6e76509612ae
parentbpo-32422: Reduce lru_cache memory usage (GH-5008) (diff)
downloadcpython-e0aef4f3cd339a405d2a7fbd35a50afa64834f84.tar.gz
cpython-e0aef4f3cd339a405d2a7fbd35a50afa64834f84.tar.bz2
cpython-e0aef4f3cd339a405d2a7fbd35a50afa64834f84.zip
bpo-31721: Allow Future._log_traceback to only be set to False (#5009)
-rw-r--r--Lib/asyncio/futures.py22
-rw-r--r--Lib/test/test_asyncio/test_futures.py5
-rw-r--r--Lib/test/test_asyncio/test_tasks.py9
-rw-r--r--Misc/NEWS.d/next/Library/2017-12-25-11-09-46.bpo-31721.5gM972.rst2
-rw-r--r--Modules/_asynciomodule.c5
5 files changed, 37 insertions, 6 deletions
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 @@ class Future:
' '.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 @@ class Future:
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 @@ class Future:
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 @@ class Future:
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 @@ class Future:
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 @@ class Future:
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 @@ class BaseFutureTests:
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 @@ class BaseTaskTests:
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;
}