OLD | NEW |
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 functools | 7 import functools |
| 8 import logging |
7 import os | 9 import os |
8 import sys | 10 import sys |
9 import traceback | 11 import traceback |
| 12 import time |
10 import urllib | 13 import urllib |
11 | 14 |
12 from cStringIO import StringIO | 15 from cStringIO import StringIO |
13 | 16 |
14 class RecipeAbort(Exception): | 17 class RecipeAbort(Exception): |
15 pass | 18 pass |
16 | 19 |
17 | 20 |
18 class ModuleInjectionError(AttributeError): | 21 class ModuleInjectionError(AttributeError): |
19 pass | 22 pass |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 self.lines[-1].write(s) | 186 self.lines[-1].write(s) |
184 break | 187 break |
185 self.lines[-1].write(s[:i]) | 188 self.lines[-1].write(s[:i]) |
186 self.lines[-1] = self.lines[-1].getvalue() | 189 self.lines[-1] = self.lines[-1].getvalue() |
187 self.lines.append(StringIO()) | 190 self.lines.append(StringIO()) |
188 s = s[i+1:] | 191 s = s[i+1:] |
189 | 192 |
190 def close(self): | 193 def close(self): |
191 if not isinstance(self.lines[-1], basestring): | 194 if not isinstance(self.lines[-1], basestring): |
192 self.lines[-1] = self.lines[-1].getvalue() | 195 self.lines[-1] = self.lines[-1].getvalue() |
| 196 |
| 197 |
| 198 class exponential_retry(object): |
| 199 """Decorator which retries the function if an exception is encountered.""" |
| 200 |
| 201 def __init__(self, retries=None, delay=None, condition=None): |
| 202 """Creates a new exponential retry decorator. |
| 203 |
| 204 Args: |
| 205 retries (int): Maximum number of retries before giving up. |
| 206 delay (datetime.timedelta): Amount of time to wait before retrying. This |
| 207 will double every retry attempt (exponential). |
| 208 condition (func): If not None, a function that will be passed the |
| 209 exception as its one argument. Retries will only happen if this |
| 210 function returns True. If None, retries will always happen. |
| 211 """ |
| 212 self.retries = retries or 5 |
| 213 self.delay = delay or datetime.timedelta(seconds=1) |
| 214 self.condition = condition or (lambda e: True) |
| 215 |
| 216 def __call__(self, f): |
| 217 @functools.wraps(f) |
| 218 def wrapper(*args, **kwargs): |
| 219 retry_delay = self.delay |
| 220 for i in xrange(self.retries): |
| 221 try: |
| 222 return f(*args, **kwargs) |
| 223 except Exception as e: |
| 224 if (i+1) >= self.retries or not self.condition(e): |
| 225 raise |
| 226 logging.exception('Exception encountered, retrying in %s', |
| 227 retry_delay) |
| 228 time.sleep(retry_delay.total_seconds()) |
| 229 retry_delay *= 2 |
| 230 return wrapper |
OLD | NEW |