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

Side by Side 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, 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 unified diff | Download patch
OLDNEW
1 # Copyright 2013 The LUCI Authors. All rights reserved. 1 # Copyright 2013 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0 2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file. 3 # that can be found in the LICENSE file.
4 4
5 import contextlib 5 import contextlib
6 import datetime 6 import datetime
7 import functools 7 import functools
8 import logging 8 import logging
9 import os 9 import os
10 import sys 10 import sys
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 try: 221 try:
222 return f(*args, **kwargs) 222 return f(*args, **kwargs)
223 except Exception as e: 223 except Exception as e:
224 if (i+1) >= self.retries or not self.condition(e): 224 if (i+1) >= self.retries or not self.condition(e):
225 raise 225 raise
226 logging.exception('Exception encountered, retrying in %s', 226 logging.exception('Exception encountered, retrying in %s',
227 retry_delay) 227 retry_delay)
228 time.sleep(retry_delay.total_seconds()) 228 time.sleep(retry_delay.total_seconds())
229 retry_delay *= 2 229 retry_delay *= 2
230 return wrapper 230 return wrapper
231
232
233 class MultiException(Exception):
234 """An exception that aggregates multiple exceptions and summarizes them."""
235
236 def __init__(self):
237 self._inner = []
238
239 def __nonzero__(self):
240 return bool(self._inner)
241
242 def __len__(self):
243 return len(self._inner)
244
245 def __getitem__(self, key):
246 return self._inner[key]
247
248 def __iter__(self):
249 return iter(self._inner)
250
251 def __str__(self):
252 if len(self._inner) == 0:
253 text = 'No exceptions'
254 elif len(self._inner) == 1:
255 text = str(self._inner[0])
256 else:
257 text = str(self._inner[0]) + ', and %d more...' % (len(self._inner)-1)
258 return '%s(%s)' % (type(self).__name__, text)
259
260 def append(self, exc):
261 assert isinstance(exc, BaseException)
262 self._inner.append(exc)
263
264 def raise_if_any(self):
265 if self._inner:
266 raise self
267
268 @contextlib.contextmanager
269 def catch(self, *exc_types):
270 """ContextManager that catches any exception raised during its execution
271 and adds them to the MultiException.
272
273 Args:
274 exc_types (list): A list of exception classes to catch. If empty,
275 Exception will be used.
276 """
277 exc_types = exc_types or (Exception,)
278 try:
279 yield
280 except exc_types as e:
281 self.append(e)
282
283
284 @contextlib.contextmanager
285 def defer_exceptions_for(it, fn, *exc_types):
286 """Executes "fn" for each element in "it". Any exceptions thrown by "fn" will
287 be deferred until the end of "it", then raised as a single MultiException.
288
289 Args:
290 it (iterable): An iterable to traverse.
291 fn (callable): A function to call for each element in "it".
292 exc_types (list): An optional list of specific exception types to handle.
293 If empty, Exception will be used.
294 """
295 mexc = MultiException()
296 for e in it:
297 with mexc.catch(*exc_types):
298 fn(e)
299 mexc.raise_if_any()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698