From b0fa3b2c9aeafa965d09fb43bbe0eeac8e828c0e Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 28 Aug 2013 22:54:29 -0700 Subject: [PATCH] Combine multiple exceptions into a linked one When multiple tasks running at the same time throw exceptions previously we would not be able to rethrow all the combined ones which was suboptimal. Instead of doing that create a combined linked exception that can retain all of the exceptions that were thrown and rethrow that in the situation where more than one task fails. Change-Id: I8f882e0d58caa189d6bff2e33b0bc30c4cee553d --- taskflow/exceptions.py | 31 ++++++++++++++++++++++++++++++ taskflow/patterns/threaded_flow.py | 11 ++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/taskflow/exceptions.py b/taskflow/exceptions.py index 7f572ff3..39819a22 100644 --- a/taskflow/exceptions.py +++ b/taskflow/exceptions.py @@ -16,6 +16,10 @@ # License for the specific language governing permissions and limitations # under the License. +import StringIO + +import traceback + class TaskFlowException(Exception): """Base class for exceptions emitted from this library.""" @@ -27,6 +31,33 @@ class Duplicate(TaskFlowException): pass +class LinkedException(TaskFlowException): + """A linked chain of many exceptions.""" + def __init__(self, message, cause, tb): + super(LinkedException, self).__init__(message) + self.cause = cause + self.tb = tb + self.next = None + + @classmethod + def link(cls, exc_infos): + first = None + previous = None + for exc_info in exc_infos: + if not all(exc_info) or not len(exc_infos) == 3: + raise ValueError("Invalid exc_info") + buf = StringIO.StringIO() + traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], + file=buf) + exc = cls(str(exc_info[1]), exc_info[1], buf.getvalue()) + if previous is not None: + previous.next = exc + else: + first = exc + previous = exc + return first + + class StorageError(TaskFlowException): """Raised when logbook can not be read/saved/deleted.""" diff --git a/taskflow/patterns/threaded_flow.py b/taskflow/patterns/threaded_flow.py index 6869b63f..e9e2380b 100644 --- a/taskflow/patterns/threaded_flow.py +++ b/taskflow/patterns/threaded_flow.py @@ -349,11 +349,12 @@ class Flow(flow.Flow): except exc.InvalidStateException: pass finally: - # TODO(harlowja): re-raise a combined exception when - # there are more than one failures?? - for f in failures: - if all(f.exc_info): - raise f.exc_info[0], f.exc_info[1], f.exc_info[2] + if len(failures) > 1: + exc_infos = [f.exc_info for f in failures] + raise exc.LinkedException.link(exc_infos) + else: + f = failures[0] + raise f.exc_info[0], f.exc_info[1], f.exc_info[2] def handle_results(): # Isolate each runner state into groups so that we can easily tell