Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(705)

Unified Diff: recipe_engine/util.py

Issue 2265673002: Add LogDog / annotation protobuf support. (Closed) Base URL: https://github.com/luci/recipes-py@step-formal-struct
Patch Set: Code review comments. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: recipe_engine/util.py
diff --git a/recipe_engine/util.py b/recipe_engine/util.py
index 91c5192c8c7ccad7d5da3e1b04b327b3a796e8df..9b36ca5402a4256e175e6a70ebb74267353168cd 100644
--- a/recipe_engine/util.py
+++ b/recipe_engine/util.py
@@ -228,3 +228,72 @@ class exponential_retry(object):
time.sleep(retry_delay.total_seconds())
retry_delay *= 2
return wrapper
+
+
+class MultiException(Exception):
+ """An exception that aggregates multiple exceptions and summarizes them."""
+
+ def __init__(self):
+ self._inner = []
+
+ def __nonzero__(self):
+ return bool(self._inner)
+
+ def __len__(self):
+ return len(self._inner)
+
+ def __getitem__(self, key):
+ return self._inner[key]
+
+ def __iter__(self):
+ return iter(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 append(self, exc):
+ assert isinstance(exc, BaseException)
+ self._inner.append(exc)
+
+ def raise_if_any(self):
+ if self._inner:
+ raise self
+
+ @contextlib.contextmanager
+ def catch(self, *exc_types):
+ """ContextManager that catches any exception raised during its execution
+ and adds them to the MultiException.
+
+ Args:
+ exc_types (list): A list of exception classes to catch. If empty,
+ Exception will be used.
+ """
+ exc_types = exc_types or (Exception,)
+ try:
+ yield
+ except exc_types as e:
+ self.append(e)
+
+
+@contextlib.contextmanager
+def defer_exceptions_for(it, fn, *exc_types):
+ """Executes "fn" for each element in "it". Any exceptions thrown by "fn" will
+ be deferred until the end of "it", then raised as a single MultiException.
+
+ Args:
+ it (iterable): An iterable to traverse.
+ fn (callable): A function to call for each element in "it".
+ exc_types (list): An optional list of specific exception types to handle.
+ If empty, Exception will be used.
+ """
+ mexc = MultiException()
+ for e in it:
+ with mexc.catch(*exc_types):
+ fn(e)
+ mexc.raise_if_any()

Powered by Google App Engine
This is Rietveld 408576698