Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from Dec 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 16 additions & 6 deletions Lib/asyncio/futures.py
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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 __iter__(self):
if not self.done():
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_asyncio/test_futures.py
Expand Up @@ -373,6 +373,11 @@ def test():
self.assertRaises(AssertionError, 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)
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_asyncio/test_tasks.py
Expand Up @@ -623,6 +623,15 @@ async def task():
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
Expand Down
@@ -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.
5 changes: 5 additions & 0 deletions Modules/_asynciomodule.c
Expand Up @@ -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;
}
Expand Down