Handling exceptions inside context managers

Quoting __exit__,

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

By default, if you don’t return a value explicitly from a function, Python will return None, which is a falsy value. In your case, __exit__ returns None and that is why the exeception is allowed to flow past the __exit__.

So, return a truthy value, like this

class retry(object):

    def __init__(self, retries=0):
        ...


    def __enter__(self):
        ...

    def __exit__(self, exc_type, exc_val, traceback):
        print 'Attempts', self.attempts
        print exc_type, exc_val
        return True                                   # or any truthy value

with retry(retries=3):
    print ok

the output will be

Attempts 1
<type 'exceptions.NameError'> name 'ok' is not defined

If you want to have the retry functionality, you can implement that with a decorator, like this

def retry(retries=3):
    left = {'retries': retries}

    def decorator(f):
        def inner(*args, **kwargs):
            while left['retries']:
                try:
                    return f(*args, **kwargs)
                except NameError as e:
                    print e
                    left['retries'] -= 1
                    print "Retries Left", left['retries']
            raise Exception("Retried {} times".format(retries))
        return inner
    return decorator


@retry(retries=3)
def func():
    print ok

func()

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)