[Python-ideas] Retrying EAFP without DRY

Jim Jewett jimjjewett at gmail.com
Wed Jan 25 17:18:49 CET 2012


On Tue, Jan 24, 2012 at 1:01 PM, Chris Rebert <pyideas at rebertia.com> wrote:
>> On 24 Jan 2012, at 14:06, Nick Coghlan <ncoghlan at gmail.com> wrote:
>>> On Tue, Jan 24, 2012 at 9:36 PM, Paul Moore <p.f.moore at gmail.com> wrote:
>>>> [ sees value in a RetryStrategy construct of some sort]

>>> You just need to move the pause inside the iterator:
>>>
>>>    def backoff(attempts, first_delay, scale=2):
>>>        delay = first_delay
>>>        for attempt in range(1, attempts+1):
>>>            yield attempt
>>>            time.sleep(delay)
>>>            delay *= 2
>>>
>>>    for __ in backoff(MAX_ATTEMPTS, 5):
>>>       try:
>>>           response = urllib2.urlopen(url)
>>>       except urllib2.HTTPError as e:
>>>           if e.code == 503:  # Service Unavailable.
>>>               continue
>>>           raise
>>>       break

> On Tue, Jan 24, 2012 at 7:21 AM, Jakob Bowyer <jkbbwr at gmail.com> wrote:
>> Would this not be better expressed as a context manager?
>>
>> with backoff(maxattempts, 5):
>>    # do stuff

> It can't be.

It really should be, though.  What do to on a fixable or temporary
failure is pretty clearly an execution context.

> The `with` statement always executes its block exactly once;
> the context manager(s) have no say in the matter (unless perhaps
> you count raising an exception prior to the block's execution).

    with RetryStrategy() as keepgoing:
        while keepgoing():
            ...

The catch is that keepgoing() is usually either simple enough to
inline (even without the with statement) or sufficiently complicated
that it needs to modify something the rest of the suite sees.  Passing
an object just to hold these changes adds its own code smell.

-jJ



More information about the Python-ideas mailing list