Hacker News new | ask | show | jobs
by nerdponx 3097 days ago
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.