user2380153
user2380153

Reputation: 173

More concise way to do something a maximum of X times, raising an exception if it takes X times to run the command

Is there a more concise way to perform this in Python:

tries = 10
for x in range(tries):
    try:
        foo()
        break
    except Exception as e:
        if x == tries-1:
            raise e

The point of it is that sometimes the operation fails (for whatever reason), but we want to allow for a number of retries before raising an exception.

Upvotes: 3

Views: 197

Answers (5)

9000
9000

Reputation: 40894

A usual way to make something more concise is to factor it out.

def retrying(max_attempts, func, *args, **kwargs):
  attempts_left = max_attempts
  while attempts_left:  # could be 'while True', but an extra check won't hurt
    try:
      return func(*args, **kwargs)
    except SomeException:
      attempts_left -= 1
      if not attempts_left:
        raise

The above code only catches SomeException and not Exception, else it would keep retrying when you have an undefined identifier in your code. Usually catching all exceptions is a bad idea, unless you re-raise them immediately. The lone raise preserves the func's stack trace. You could pass the list of exceptions to catch as a parameter.

The code above does most sense if you have several places where you need to retry actions. Then you just write retrying(3, foo, 1, b=2) instead of foo(1, b=2).

Upvotes: 4

mknecht
mknecht

Reputation: 1265

This is the same as @9000 solution, keeping it's advantages: encapsulated functionality, preserved stack trace. It changes two things: Using a for-loop instead of a while-loop and counting down instead of up.

def retrying(max_attempts, func, *args, **kwargs):
    for attempts_left in reversed(range(max_attempts)):
        try:
            return func(*args, **kwargs)
        except ValueError:
            if not attempts_left:
                raise

For my taste/brain, the for-loop is easier to grasp: a collection is being processed until its end is reached. while-loops always make me search for the catch.

Counting down matches my intuitive understanding better.

A matter of taste, I guess, but for the reasons above I definitely prefer this one.

Upvotes: 0

Totem
Totem

Reputation: 7369

Essentially the same but slightly more concise...

x = 10
while x > 0:
    try:
        foo()
        break
    except Error:
        if x == 1: raise Exception('')
    x -= 1

Upvotes: 0

mhlester
mhlester

Reputation: 23251

Try this:

tries = 10
for _ in range(tries):
    try:
        foo()
    except Exception as e:
        pass
    else:
        break
else:
    raise e

Upvotes: 3

dornhege
dornhege

Reputation: 1500

Shouldn't this do what you want:

tries = 10
for x in range(tries):
    try:
        foo()
        break
    except Exception as e:
        pass

The first time foo() succeeds you break out and are done.

Upvotes: -2

Related Questions