|
It looks like the Ruby-specific elements here are (1) the 'retry' statement, and (2) the ability to yield a block twice (Python's equivalent `with` syntax explicitly prohibits re-yielding a block). (1) might just be a matter of taste -- that 'retry', to me, makes me now have to step back and think "oh, I'm retrying something now? what exactly is being retried here?" Having it all wrapped up in a 'while' loop makes the intent explicit right from the get-go. (2) I agree is nice. It would be great if you could do this: with backoff():
connect_to_a_thing()
but you're not allowed to 'yield' twice inside a context manager. However you can get damn close (and fully composable) by wrapping the connection logic and the retry logic in functions: def connect_to_a_thing(url, access_token):
...
def backoff(connector, wait=5, exponent=1.5, *args, **kwargs):
success = False
while not success:
try:
connector(*args, **kwargs)
except BackoffError:
sleep(wait)
wait = wait ** exponent
else:
success = True
backoff(connect_to_a_thing, url, access_token)
Or you could be even more explicit with recursion: def backoff(connector, wait=5, exponent=1.5, *args, **kwargs):
try:
connector(*args, **kwargs)
except BackoffError:
sleep(wait)
backoff(connector, wait ** exponent, exponent, *args, **kwargs)
As for your point about loop indices, Python generally has wonderful support for not worrying about loop indices and counters. I'm also not sure how that applies in this case; you still need to increment the value of 'wait'. Then again... def exponentiate_forever(value, exponent):
while True:
yield value
value = value ** exponent
def backoff(connector, wait=5, exponent=1.5, *args, **kwargs):
for wait in exponentiate_forever(wait, exponent):
try:
connector(*args, **kwargs)
except BackoffError:
sleep(wait)
else:
break
But please don't do that. |