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
This commit is contained in:
Joshua Harlow
2013-08-28 22:54:29 -07:00
committed by Joshua Harlow
parent ab9ca13055
commit b0fa3b2c9a
2 changed files with 37 additions and 5 deletions

View File

@@ -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."""

View File

@@ -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