From 4f7ceedb8a7742e52b0436a4160c7c44665a2597 Mon Sep 17 00:00:00 2001 From: Alex Gr?nholm Date: Mon, 8 Sep 2014 06:46:34 +0300 Subject: Added the set_exception_info() and exception_info() methods to Future which provide the missing traceback information on Python 2.x --- concurrent/futures/_base.py | 47 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) (limited to 'concurrent/futures/_base.py') diff --git a/concurrent/futures/_base.py b/concurrent/futures/_base.py index 8ed69b7..6f0c0f3 100644 --- a/concurrent/futures/_base.py +++ b/concurrent/futures/_base.py @@ -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,8 +408,9 @@ class Future(object): else: raise TimeoutError() - def exception(self, timeout=None): - """Return the exception raised by the call that the future represents. + 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 @@ -422,22 +426,40 @@ 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 + 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 + return self._exception, self._traceback else: raise TimeoutError() + def exception(self, timeout=None): + """Return the exception 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. + """ + 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): """Mark the future as running or process any cancel notifications. @@ -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.""" -- cgit v1.2.1