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

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: Stronger flush meta logic, moar test. Created 4 years, 2 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
« no previous file with comments | « recipe_engine/unittests/util_test.py ('k') | recipe_modules/step/api.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: recipe_engine/util.py
diff --git a/recipe_engine/util.py b/recipe_engine/util.py
index 91c5192c8c7ccad7d5da3e1b04b327b3a796e8df..7fe739ee862c6d66a3ebfd833dd5e05049ddae29 100644
--- a/recipe_engine/util.py
+++ b/recipe_engine/util.py
@@ -140,7 +140,8 @@ BUG_LINK = (
'https://code.google.com/p/chromium/issues/entry?%s' % urllib.urlencode({
'summary': 'Recipe engine bug: unexpected failure',
'comment': 'Link to the failing build and paste the exception here',
- 'labels': 'Infra,Infra-Area-Recipes,Pri-1,Restrict-View-Google,Infra-Troopers',
+ 'labels': 'Infra,Infra-Area-Recipes,Pri-1,Restrict-View-Google,'
+ 'Infra-Troopers',
'cc': 'martiniss@chromium.org,iannucci@chromium.org',
}))
@@ -228,3 +229,91 @@ 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."""
+
+ class Builder(object):
+ """Iteratively constructs a MultiException."""
+
+ def __init__(self):
+ self._exceptions = []
+
+ def append(self, exc):
+ if exc is not None:
+ self._exceptions.append(exc)
+
+ def get(self):
+ """Returns (MultiException or None): The constructed MultiException.
+
+ If no exceptions have been appended, None will be returned.
+ """
+ return MultiException(*self._exceptions) if self._exceptions else (None)
+
+ def raise_if_any(self):
+ mexc = self.get()
+ if mexc is not None:
+ raise mexc
+
+ @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 exc:
+ self.append(exc)
+
+
+ def __init__(self, *base):
+ super(MultiException, self).__init__()
+
+ # Determine base Exception text.
+ if len(base) == 0:
+ self.message = 'No exceptions'
+ elif len(base) == 1:
+ self.message = str(base[0])
+ else:
+ self.message = str(base[0]) + ', and %d more...' % (len(base)-1)
+ self._inner = base
+
+ 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):
+ return '%s(%s)' % (type(self).__name__, self.message)
+
+
+@contextlib.contextmanager
+def map_defer_exceptions(fn, it, *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:
+ fn (callable): A function to call for each element in "it".
+ it (iterable): An iterable to traverse.
+ exc_types (list): An optional list of specific exception types to defer.
+ If empty, Exception will be used. Any Exceptions not referenced by this
+ list will skip deferring and be immediately raised.
+ """
+ mexc_builder = MultiException.Builder()
+ for e in it:
+ with mexc_builder.catch(*exc_types):
+ fn(e)
+ mexc_builder.raise_if_any()
« no previous file with comments | « recipe_engine/unittests/util_test.py ('k') | recipe_modules/step/api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698