Index: tools/push-to-trunk/common_includes.py |
diff --git a/tools/push-to-trunk/common_includes.py b/tools/push-to-trunk/common_includes.py |
index 4f77c6b4ac728b202db79cad5ff0e06664b07b70..668f95e0554f494363749858eb6c6ee7afba0455 100644 |
--- a/tools/push-to-trunk/common_includes.py |
+++ b/tools/push-to-trunk/common_includes.py |
@@ -31,6 +31,7 @@ import re |
import subprocess |
import sys |
import textwrap |
+import time |
import urllib2 |
PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" |
@@ -173,6 +174,7 @@ def MakeChangeLogBugReference(body): |
# Some commands don't like the pipe, e.g. calling vi from within the script or |
# from subscripts like git cl upload. |
def Command(cmd, args="", prefix="", pipe=True): |
+ # TODO(machenbach): Use timeout. |
cmd_line = "%s %s %s" % (prefix, cmd, args) |
print "Command: %s" % cmd_line |
try: |
@@ -200,6 +202,9 @@ class SideEffectHandler(object): |
finally: |
url_fh.close() |
+ def Sleep(seconds): |
+ time.sleep(seconds) |
+ |
DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() |
@@ -231,6 +236,36 @@ class Step(object): |
def RunStep(self): |
raise NotImplementedError |
+ def Retry(self, cb, retry_on=None, wait_plan=None): |
+ """ Retry a function. |
+ Params: |
+ cb: The function to retry. |
+ retry_on: A callback that takes the result of the function and returns True |
Jakob Kummerow
2013/11/28 13:47:27
nit: 80col
Michael Achenbach
2013/11/28 14:28:17
Done.
|
+ if the function should be retried. A function throwing an |
+ exception is always retried. |
+ wait_plan: A list of waiting delays between retries in seconds. The maximum |
Jakob Kummerow
2013/11/28 13:47:27
nit: 80col
Michael Achenbach
2013/11/28 14:28:17
Done.
|
+ number of retries is len(wait_plan). |
+ """ |
+ retry_on = retry_on or (lambda x: False) |
+ wait_plan = list(wait_plan or []) |
Jakob Kummerow
2013/11/28 13:47:27
I don't think the explicit list() conversion is ne
Michael Achenbach
2013/11/28 14:28:17
I'd rather keep it, since the method has side effe
|
+ # Sentinel for the first try. |
Jakob Kummerow
2013/11/28 13:47:27
s/first/last/! This is seriously misleading.
Here
Michael Achenbach
2013/11/28 14:28:17
I take it :) Just added an initialization of got_e
|
+ wait_plan.append(0) |
+ wait_plan.reverse() |
+ while wait_plan: |
+ got_exception = False |
+ try: |
+ result = cb() |
+ except Exception: |
+ got_exception = True |
+ if got_exception or retry_on(result): |
+ wait_time = wait_plan.pop() |
+ print "Waiting for %f seconds." % wait_time |
+ self._side_effect_handler.Sleep(wait_time) |
+ print "Retrying..." |
+ else: |
+ return result |
+ raise Exception("Retried too often. Giving up.") |
+ |
def ReadLine(self, default=None): |
# Don't prompt in forced mode. |
if self._options and self._options.f and default is not None: |
@@ -239,15 +274,18 @@ class Step(object): |
else: |
return self._side_effect_handler.ReadLine() |
- def Git(self, args="", prefix="", pipe=True): |
- return self._side_effect_handler.Command("git", args, prefix, pipe) |
+ def Git(self, args="", prefix="", pipe=True, retry_on=None): |
+ cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) |
+ return self.Retry(cmd, retry_on, [5, 30]) |
def Editor(self, args): |
return self._side_effect_handler.Command(os.environ["EDITOR"], args, |
pipe=False) |
- def ReadURL(self, url): |
- return self._side_effect_handler.ReadURL(url) |
+ def ReadURL(self, url, retry_on=None, wait_plan=None): |
+ wait_plan = wait_plan or [3, 10, 30] |
Jakob Kummerow
2013/11/28 13:47:27
I'd give the server much more time. Maybe [3, 60,
Michael Achenbach
2013/11/28 14:28:17
Done.
|
+ cmd = lambda: self._side_effect_handler.ReadURL(url) |
+ return self.Retry(cmd, retry_on, wait_plan) |
def Die(self, msg=""): |
if msg != "": |