Chromium Code Reviews| Index: recipe_engine/util.py |
| diff --git a/recipe_engine/util.py b/recipe_engine/util.py |
| index cb3257b37f1f98027aae40c9b4b699c15d88c26a..cf355cfab4787f04b1a5df9476ccb89dcd936e6e 100644 |
| --- a/recipe_engine/util.py |
| +++ b/recipe_engine/util.py |
| @@ -190,3 +190,47 @@ class StringListIO(object): |
| def close(self): |
| if not isinstance(self.lines[-1], basestring): |
| self.lines[-1] = self.lines[-1].getvalue() |
| + |
| + |
| +class MultiException(Exception): |
| + """An exception that aggregates multiple exceptions and summarizes them.""" |
| + |
| + def __init__(self): |
| + self._inner = [] |
| + |
| + def append(self, exc): |
| + assert isinstance(exc, BaseException) |
| + self._inner.append(exc) |
| + |
| + def __nonzero__(self): |
| + return bool(self._inner) |
| + |
| + def __str__(self): |
| + if len(self._inner) == 0: |
| + text = 'No exceptions' |
| + elif len(self._inner) == 1: |
| + text = str(self._inner[0]) |
| + else: |
| + text = str(self._inner[0]) + ', and %d more...' % (len(self._inner)-1) |
| + return '%s(%s)' % (type(self).__name__, text) |
| + |
| + def raise_if_any(self): |
| + if self: |
|
martiniss
2016/09/01 21:59:48
Shouldn't this be "if self._inner"?
dnj
2016/09/07 17:54:58
Done.
|
| + raise self |
| + |
| + @contextlib.contextmanager |
| + def catch(self): |
| + try: |
| + yield |
| + except Exception as e: |
|
martiniss
2016/09/01 21:59:48
Can you make the exception class a required parame
dnj
2016/09/07 17:54:58
Sure.
However, note the only user ("defer_excepti
|
| + self.append(e) |
| + |
| + |
| +def defer_exceptions_for(gen): |
| + mexc = MultiException() |
| + for e in gen: |
| + try: |
| + yield e |
| + except Exception as e: |
| + exc.append(e) |
| + mexc.raise_if_any() |