Added the set_exception_info() and exception_info() methods to Future which provide the missing traceback information on Python 2.x
This commit is contained in:
parent
7b6ec83427
commit
6342a77a56
8
CHANGES
8
CHANGES
|
@ -1,3 +1,11 @@
|
|||
2.2.0
|
||||
=====
|
||||
|
||||
- Added the set_exception_info() and exception_info() methods to Future
|
||||
to enable extraction of tracebacks on Python 2.x
|
||||
- Added support for Future.set_exception_info() to ThreadPoolExecutor
|
||||
|
||||
|
||||
2.1.6
|
||||
=====
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import logging
|
|||
import threading
|
||||
import time
|
||||
|
||||
from concurrent.futures._compat import reraise
|
||||
|
||||
try:
|
||||
from collections import namedtuple
|
||||
except ImportError:
|
||||
|
@ -290,6 +292,7 @@ class Future(object):
|
|||
self._state = PENDING
|
||||
self._result = None
|
||||
self._exception = None
|
||||
self._traceback = None
|
||||
self._waiters = []
|
||||
self._done_callbacks = []
|
||||
|
||||
|
@ -353,7 +356,7 @@ class Future(object):
|
|||
|
||||
def __get_result(self):
|
||||
if self._exception:
|
||||
raise self._exception
|
||||
reraise(self._exception, self._traceback)
|
||||
else:
|
||||
return self._result
|
||||
|
||||
|
@ -405,6 +408,39 @@ class Future(object):
|
|||
else:
|
||||
raise TimeoutError()
|
||||
|
||||
def exception_info(self, timeout=None):
|
||||
"""Return a tuple of (exception, traceback) raised by the call that the
|
||||
future represents.
|
||||
|
||||
Args:
|
||||
timeout: The number of seconds to wait for the exception if the
|
||||
future isn't done. If None, then there is no limit on the wait
|
||||
time.
|
||||
|
||||
Returns:
|
||||
The exception raised by the call that the future represents or None
|
||||
if the call completed without raising.
|
||||
|
||||
Raises:
|
||||
CancelledError: If the future was cancelled.
|
||||
TimeoutError: If the future didn't finish executing before the given
|
||||
timeout.
|
||||
"""
|
||||
with self._condition:
|
||||
if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
|
||||
raise CancelledError()
|
||||
elif self._state == FINISHED:
|
||||
return self._exception, self._traceback
|
||||
|
||||
self._condition.wait(timeout)
|
||||
|
||||
if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
|
||||
raise CancelledError()
|
||||
elif self._state == FINISHED:
|
||||
return self._exception, self._traceback
|
||||
else:
|
||||
raise TimeoutError()
|
||||
|
||||
def exception(self, timeout=None):
|
||||
"""Return the exception raised by the call that the future represents.
|
||||
|
||||
|
@ -422,21 +458,7 @@ class Future(object):
|
|||
TimeoutError: If the future didn't finish executing before the given
|
||||
timeout.
|
||||
"""
|
||||
|
||||
with self._condition:
|
||||
if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
|
||||
raise CancelledError()
|
||||
elif self._state == FINISHED:
|
||||
return self._exception
|
||||
|
||||
self._condition.wait(timeout)
|
||||
|
||||
if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
|
||||
raise CancelledError()
|
||||
elif self._state == FINISHED:
|
||||
return self._exception
|
||||
else:
|
||||
raise TimeoutError()
|
||||
return self.exception_info(timeout)[0]
|
||||
|
||||
# The following methods should only be used by Executors and in tests.
|
||||
def set_running_or_notify_cancel(self):
|
||||
|
@ -492,19 +514,28 @@ class Future(object):
|
|||
self._condition.notify_all()
|
||||
self._invoke_callbacks()
|
||||
|
||||
def set_exception(self, exception):
|
||||
"""Sets the result of the future as being the given exception.
|
||||
def set_exception_info(self, exception, traceback):
|
||||
"""Sets the result of the future as being the given exception
|
||||
and traceback.
|
||||
|
||||
Should only be used by Executor implementations and unit tests.
|
||||
"""
|
||||
with self._condition:
|
||||
self._exception = exception
|
||||
self._traceback = traceback
|
||||
self._state = FINISHED
|
||||
for waiter in self._waiters:
|
||||
waiter.add_exception(self)
|
||||
self._condition.notify_all()
|
||||
self._invoke_callbacks()
|
||||
|
||||
def set_exception(self, exception):
|
||||
"""Sets the result of the future as being the given exception.
|
||||
|
||||
Should only be used by Executor implementations and unit tests.
|
||||
"""
|
||||
self.set_exception_info(exception, None)
|
||||
|
||||
class Executor(object):
|
||||
"""This is an abstract base class for concrete asynchronous executors."""
|
||||
|
||||
|
|
|
@ -99,3 +99,13 @@ def namedtuple(typename, field_names):
|
|||
result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if _sys.version_info[0] < 3:
|
||||
def reraise(exc, traceback):
|
||||
locals_ = {'exc_type': type(exc), 'exc_value': exc, 'traceback': traceback}
|
||||
exec('raise exc_type, exc_value, traceback', {}, locals_)
|
||||
else:
|
||||
def reraise(exc, traceback):
|
||||
# Tracebacks are embedded in exceptions in Python 3
|
||||
raise exc
|
||||
|
|
|
@ -60,8 +60,8 @@ class _WorkItem(object):
|
|||
try:
|
||||
result = self.fn(*self.args, **self.kwargs)
|
||||
except BaseException:
|
||||
e = sys.exc_info()[1]
|
||||
self.future.set_exception(e)
|
||||
e, tb = sys.exc_info()[1:]
|
||||
self.future.set_exception_info(e, tb)
|
||||
else:
|
||||
self.future.set_result(result)
|
||||
|
||||
|
|
Loading…
Reference in New Issue