Skip to content

Commit

Permalink
Allow deferred objects to name themselves
Browse files Browse the repository at this point in the history
Implement a version of retryCall (RetryCall) that does this.
  • Loading branch information
Kevin Clark committed Nov 12, 2012
1 parent 15fd48a commit 1f4ad2f
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
13 changes: 10 additions & 3 deletions src/greplin/defer/inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ def call(*args, **kwargs):
"""The new function."""
# Save and restore the context afterwards so fn() doesn't interfere with other deferreds
current = context.current()
d = InlinedCallbacks(fn(*args, **kwargs))
reprFn = None
if args[:1] and hasattr(args[0], 'describeDeferred'):
reprFn = args[0].describeDeferred
d = InlinedCallbacks(fn(*args, **kwargs), reprFn)
context.setCurrent(current)

if d.called:
Expand Down Expand Up @@ -71,13 +74,14 @@ class InlinedCallbacks(base.LowMemoryDeferred):
__slots__ = ('_current', '_generator', '_state', '_context')


def __init__(self, generator):
def __init__(self, generator, reprFn=None):
base.LowMemoryDeferred.__init__(self)
self._generator = generator
self._state = STATE_NORMAL
self._current = None
self._context = context.current()
self._step(None)
self._reprFn = reprFn


def __canceller(self, _):
Expand Down Expand Up @@ -149,4 +153,7 @@ def _handleResult(self, result):

def describeDeferred(self):
"""Describes this Deferred."""
return '%s:%s -> %s' % (self._generator.__name__, self._state, base.describeDeferred(self._current))
if self._reprFn:
return '%s:%s -> %s' % (self._reprFn(), self._state, base.describeDeferred(self._current))
else:
return '%s:%s -> %s' % (self._generator.__name__, self._state, base.describeDeferred(self._current))
43 changes: 43 additions & 0 deletions src/greplin/defer/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,46 @@ def retryCall(fn, args=None, keywordArgs=None, failureTester=None, sleepManager=
except Exception: # pylint: disable=W0703
failureTester(failure.Failure())
yield sleepManager.sleep()



class RetryCall(object):
"""Identical functionality to retryCall, but tracks the number of iterations and description of the last error"""


def __init__(self):
self.iteration = 0
self.lastError = None


@inline.callbacks
def __call__(self, fn, args=None, keywordArgs=None, failureTester=None, sleepManager=None):
"""Calls the given function, automatically retrying as necessary.
Arguments:
fn: The function to call.
failureTester: Function called with a failure. The failure should be re-raised if it is not allowable.
sleepManager: A sleep manager to control how long to sleep between retries.
args: Args to pass to the function.
keywordArgs: keywordArgs to pass to the function.
Returns:
A deferred that will be called on success.
"""
sleepManager = sleepManager or time.SleepManager()
while True:
try:
result = yield fn(*args, **keywordArgs)
defer.returnValue(result)
except Exception as e: # pylint: disable=W0703
self.lastError = e
failureTester(failure.Failure())
yield sleepManager.sleep()
finally:
self.iteration += 1


def describeDeferred(self):
"""Prints a description of what's going on in the retry call"""
return "RetryCall(iteration=%s, lastError=%s)" % (self.iteration, self.lastError)

0 comments on commit 1f4ad2f

Please sign in to comment.