Commit e0aef4f3 authored by Yury Selivanov's avatar Yury Selivanov Committed by GitHub

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

parent 3070b71e
...@@ -65,7 +65,7 @@ class Future: ...@@ -65,7 +65,7 @@ class Future:
# `yield Future()` (incorrect). # `yield Future()` (incorrect).
_asyncio_future_blocking = False _asyncio_future_blocking = False
_log_traceback = False __log_traceback = False
def __init__(self, *, loop=None): def __init__(self, *, loop=None):
"""Initialize the future. """Initialize the future.
...@@ -90,7 +90,7 @@ class Future: ...@@ -90,7 +90,7 @@ class Future:
' '.join(self._repr_info())) ' '.join(self._repr_info()))
def __del__(self): def __del__(self):
if not self._log_traceback: if not self.__log_traceback:
# set_exception() was not called, or result() or exception() # set_exception() was not called, or result() or exception()
# has consumed the exception # has consumed the exception
return return
...@@ -105,6 +105,16 @@ class Future: ...@@ -105,6 +105,16 @@ class Future:
context['source_traceback'] = self._source_traceback context['source_traceback'] = self._source_traceback
self._loop.call_exception_handler(context) 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): def get_loop(self):
"""Return the event loop the Future is bound to.""" """Return the event loop the Future is bound to."""
return self._loop return self._loop
...@@ -116,7 +126,7 @@ class Future: ...@@ -116,7 +126,7 @@ class Future:
change the future's state to cancelled, schedule the callbacks and change the future's state to cancelled, schedule the callbacks and
return True. return True.
""" """
self._log_traceback = False self.__log_traceback = False
if self._state != _PENDING: if self._state != _PENDING:
return False return False
self._state = _CANCELLED self._state = _CANCELLED
...@@ -162,7 +172,7 @@ class Future: ...@@ -162,7 +172,7 @@ class Future:
raise CancelledError raise CancelledError
if self._state != _FINISHED: if self._state != _FINISHED:
raise InvalidStateError('Result is not ready.') raise InvalidStateError('Result is not ready.')
self._log_traceback = False self.__log_traceback = False
if self._exception is not None: if self._exception is not None:
raise self._exception raise self._exception
return self._result return self._result
...@@ -179,7 +189,7 @@ class Future: ...@@ -179,7 +189,7 @@ class Future:
raise CancelledError raise CancelledError
if self._state != _FINISHED: if self._state != _FINISHED:
raise InvalidStateError('Exception is not set.') raise InvalidStateError('Exception is not set.')
self._log_traceback = False self.__log_traceback = False
return self._exception return self._exception
def add_done_callback(self, fn): def add_done_callback(self, fn):
...@@ -237,7 +247,7 @@ class Future: ...@@ -237,7 +247,7 @@ class Future:
self._exception = exception self._exception = exception
self._state = _FINISHED self._state = _FINISHED
self._schedule_callbacks() self._schedule_callbacks()
self._log_traceback = True self.__log_traceback = True
def __await__(self): def __await__(self):
if not self.done(): if not self.done():
......
...@@ -374,6 +374,11 @@ class BaseFutureTests: ...@@ -374,6 +374,11 @@ class BaseFutureTests:
test() test()
fut.cancel() 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') @mock.patch('asyncio.base_events.logger')
def test_tb_logger_abandoned(self, m_log): def test_tb_logger_abandoned(self, m_log):
fut = self._new_future(loop=self.loop) fut = self._new_future(loop=self.loop)
......
...@@ -623,6 +623,15 @@ class BaseTaskTests: ...@@ -623,6 +623,15 @@ class BaseTaskTests:
t.cancel() t.cancel()
self.assertRaises(asyncio.CancelledError, loop.run_until_complete, t) 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 test_wait_for_timeout_less_then_0_or_0_future_done(self):
def gen(): def gen():
when = yield when = yield
......
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.
...@@ -1058,6 +1058,11 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val) ...@@ -1058,6 +1058,11 @@ FutureObj_set_log_traceback(FutureObj *fut, PyObject *val)
if (is_true < 0) { if (is_true < 0) {
return -1; 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; fut->fut_log_tb = is_true;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment