Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancellation; verbosity #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions txretry/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def simpleBackoffIterator(maxResults=10, maxDelay=120.0, now=True,

class RetryingCall(object):
"""
Calls a function repeatedly, passing it args and kw args. Failures are
Calls a function repeatedly, passing it *args. Failures are
passed to a user-supplied failure testing function. If the failure is
ignored, the function is called again after a delay whose duration is
obtained from a user-supplied iterator. The start method (below)
Expand All @@ -67,14 +67,15 @@ class RetryingCall(object):
@ival failures: a list of failures received in calling the function.
@param func: The function to call.
@param args: Positional arguments to pass to the function.
@param kw: Keyword arguments to pass to the function.
@param verbose: Whether to log failures.
"""
def __init__(self, func, *args, **kw):
self._func = func
self._args = args
self._kw = kw
self._start = time.time()
self.failures = []
self._verbose = kw.get("verbose", True)
self._delay_deferred = None

def _err(self, fail):
"""An errback function for the function call.
Expand All @@ -86,7 +87,7 @@ def _err(self, fail):
self.failures.append(fail)
try:
result = self._failureTester(fail)
except:
except Exception:
self._deferred.errback()
else:
if isinstance(result, failure.Failure):
Expand All @@ -95,7 +96,8 @@ def _err(self, fail):
self._deferred.errback(result)
else:
# Schedule another call.
log.msg('RetryingCall: Ignoring failure %s' % (fail,))
if self._verbose:
log.err(fail, 'RetryingCall: attempt {} failed'.format(len(self.failures)))
self._call()

def _call(self):
Expand All @@ -105,12 +107,13 @@ def _call(self):
try:
delay = self._backoffIterator.next()
except StopIteration:
log.msg('StopIteration in RetryingCall: ran out of attempts.')
if self._verbose:
log.msg('RetryingCall: ran out of attempts.')
self._deferred.errback(self.failures[0] if self.failures else None)
else:
d = task.deferLater(reactor, delay,
self._func, *self._args, **self._kw)
d.addCallbacks(self._deferred.callback, self._err)
self._delay_deferred = task.deferLater(reactor, delay,
self._func, *self._args)
self._delay_deferred.addCallbacks(self._deferred.callback, self._err)

def start(self, backoffIterator=None, failureTester=None):
"""
Expand All @@ -123,12 +126,18 @@ def start(self, backoffIterator=None, failureTester=None):
argument (a C{failure.Failure}) that we can use to check
whether a failed call should be retried.
@return: a C{Deferred} that will fire with the result of calling
self._func with self._args and self._kw as arguments, or fail
with the first failure encountered.
self._func with self._args as arguments, or fail with the
first failure encountered.
"""
self._backoffIterator = iter(backoffIterator or
simpleBackoffIterator())
self._failureTester = failureTester or (lambda _: None)
self._deferred = defer.Deferred()

def cancel(_d):
if self._delay_deferred:
self._delay_deferred.cancel()
self._deferred = defer.Deferred(cancel)

self._call()
return self._deferred