Returning to 'try' block after catching an exception

Duncan Booth duncan.booth at invalid.invalid
Thu May 22 03:53:06 EDT 2008


bukzor <workitharder at gmail.com> wrote:

> On May 21, 4:33 pm, Karlo Lozovina <_karlo_ at _mosor.net_> wrote:
>> André <andre.robe... at gmail.com> wrote
>> innews:a9913f2d-0c1a-4492-bf58-5c7 
> 8813c4458 at s50g2000hsb.googlegroups.com:
>>
>> > How about something like the following (untested)
>>
>> > done = False
>> > while not done:
>> >   try:
>> >      some_function()
>> >      done = True
>> >   except:
>> >      some_function2()
>> >      some_function3()
>>
>> Sure, that works, but I was aiming for something more elegant and
>> Pythonic 

Catching only the specific exceptions you think you can handle would be 
more Pythonic: that way things like sys.exit() will still work inside 
some_function.

> 
> It's hard to get around a while loop if you want to conditionally
> repeat something. There's no built-in way to do what you ask.

I prefer a 'for' loop rather than 'while' so I can limit the number of 
retries.

The following may or may not be 'more pythonic', but is something I've 
used. It's a factory to generate decorators which will retry the 
decorated function. You have to specify the maximum number of retries, 
the exceptions which indicate that it is retryable, and an optional 
filter function which can try to do fixups or just specify some 
additional conditions to be tested.

class TooManyRetries(Exception):
    def __init__(self, inner):
        self.inner = inner
    def __str__(self):
        return 'Too many retries: %s' % (str(self.inner),)

def retryable(max_retries, exceptions, filter=None):
    """Creates a decorator which will retry specific exceptions.
    After there have been too many retries it raises TooManyRetries.
    """
    def decorator(f):
        def wrapper(*args, **kw):
            for i in xrange(max_retries):
                try:
                    return f(*args, **kw)
                except exceptions, e:
                    exc_info = sys.exc_info()
                    # Check for retry even the last time in case the
                    # filter wants to do any logging.
                    if filter is not None and filter(e,
                        i==max_retries-1):
                        pass
                    else:
                        raise
            # If we get here we have exceeded the maximum number of
            # retries.
            raise TooManyRetries, e, exc_info[2]

        wrapper.__name__ = f.__name__
        return wrapper
    return decorator

def isConflictError(error, lasttime):
    if (error.code==500
        and error.hdrs['Bobo-Exception-Type']=='ConflictError'):
        timestamp("Conflict Error", error.filename)
        return True
    return False

retryConflict = retryable(3, HTTPError, isConflictError)

# ----- some code showing it in use -----------
from mechanize import Browser
browser = Browser()

submit = retryConflict(browser.submit)
follow_link = retryConflict(browser.follow_link)

@retryConflict
def openpage(message, url, name=None):
    global PASSWORD
    if PASSWORD is None:
        start = time.time()
        PASSWORD = getpass.getpass()
        #PASSWORD = raw_input("password?")
        end = time.time()
        logger.LASTTIME += end-start # Don't include input in timestamp.

    resp = browser.open(url)
    follow_relay()

    if "ourloginpage" in browser.geturl():
        # Not yet logged in
        timestamp("Got login form")
        browser.select_form(nr=0)
        browser["user"] = USERID
        browser["pass"] = PASSWORD
        resp = browser.submit()
        follow_relay()
        if 'query' in [f.name for f in browser.forms()]:
            if name=="query":
                soup = BeautifulSoup(browser.response().read())
                err = soup.find('td', 'tdbodywarning')
                sys.exit(''.join(err.p.contents).strip())

    timestamp(message, name, url)
    return resp



More information about the Python-list mailing list