HWRF  trunk@4391
retry.py
1 """!Contains retry_io() which automates retrying operations."""
2 
3 ##@var __all__
4 # Symbols exported by "from produtil.retry import *"
5 __all__=['retry_io']
6 
7 import time
8 import logging
9 import random
10 
11 def retry_io(max_tries,sleep_time,operation,opargs=[],logger=None,
12  fail=None,failargs=[],giveup=None,giveupargs=[],randsleep=True,
13  backoff=1.3,first_warn=0,giveup_quiet=False):
14  """!This function automates retrying an unreliable operation
15  several times until it succeeds. This subroutine will retry the
16  operation up to a maximum number of times. If the operation fails
17  too many times, then the last exception thrown by the operation is
18  passed on (raised) to the caller.
19 
20  @param max_tries Maximum number of times to attempt the operation (mandatory)
21  @param sleep_time Time to sleep between tries
22  @param operation A function or callable object that may thrown an Exception
23  @param opargs A list containing arguments to the operation
24  @param logger A logging.Logger object to use for logging, or None
25  to disable logging.
26  @param fail A string to print, or a function to call, when
27  the operation fails but more retries are possible
28  @param failargs Optional: a list of arguments to fail, or None to disable
29  @param giveup A string to print, or a function to call when the
30  operation fails too many times, causing retry_io
31  to give up. Default: same as fail
32  @param giveupargs Optional: a list of arguments to giveup, or None to disable
33  @param randsleep Set to True (default) to enable an exponential backoff
34  algorithm, which will increase the sleep time between tries
35  @param backoff The exponent for the exponential backoff algorithm
36  @param first_warn The first failure at which to warn via the logger
37  @param giveup_quiet If True, a WARNING-level message is sent to the logger
38  if the operation fails more than max_tries times.
39  @return The return value of the operation.
40  @note If fail or giveup are functions, they are passed the contents of
41  failargs (default: opargs) or giveupargs (default: failargs or
42  opargs) with several additional arguments appended. Those
43  arguments are the exception that was caught, the number of
44  attempts so far, the max_tries, the sleep_time, and then a boolean
45  that is true iff the operation is about to be retried."""
46 
47  # Handle default arguments:
48  if fail is not None:
49  if failargs is None: failargs=opargs
50  if giveup is None: giveup=fail
51  if giveup is not None:
52  if giveupargs is None: giveupargs=failargs
53 
54  if sleep_time is None:
55  sleep_time=0.1
56  sleepme=sleep_time
57 
58  for ntry in xrange(max_tries):
59  try:
60  if logger is not None:
61  logger.debug('%s(%s)'%(repr(operation),repr(opargs)))
62  return operation(*opargs)
63  except(Exception) as e:
64  if(ntry<max_tries-1):
65  # Failed but have not given up yet.
66  if logger is not None:
67  logger.debug('Failed but have not given up: %s'%(str(e),),
68  exc_info=True)
69  if sleep_time is not None:
70  if randsleep:
71  sleepmax=min(sleep_time,0.05) * \
72  min(max(1.0,backoff**ntry),50.0)
73  sleepme=random.uniform(sleepmax/2.0,sleepmax)
74  if isinstance(fail,basestring):
75  if logger is not None and ntry>=first_warn:
76  logger.info("%s (try %d/%d; sleep %.3f and retry): %s"%\
77  (fail,ntry+1,max_tries,sleepme,repr(e)))
78  elif fail is not None:
79  arglist=failargs[:]
80  arglist.extend([e,ntry+1,max_tries,sleep_time,True])
81  if logger is not None:
82  logger.debug('arglist to fail (1): '+repr(arglist))
83  fail(*arglist)
84  time.sleep(max(0.05,sleepme))
85  else:
86  # Gave up.
87  if logger is not None:
88  logger.debug('Failed and gave up: %s'%(str(e),),
89  exc_info=True)
90  if isinstance(giveup,basestring) and not giveup_quiet:
91  if logger is not None:
92  logger.warning("%s (giving up after %d tries): %s"%\
93  (giveup,ntry+1,repr(e)),exc_info=True)
94  elif giveup is not None:
95  arglist=giveupargs[:]
96  arglist.extend([e,ntry+1,max_tries,sleep_time,False])
97  if logger is not None:
98  logger.debug('arglist to fail (2): '+repr(arglist))
99  if isinstance(fail,basestring):
100  if giveup_quiet:
101  logger.warning(fail)
102  else:
103  fail(*arglist)
104  raise
def retry_io
This function automates retrying an unreliable operation several times until it succeeds.
Definition: retry.py:13