python flow control for conditionally retrying an operation - python

I'm attempting an operation in Python, and if it fails in certain ways I'd like to retry up to 10 times. If it fails in any other way I'd like it to fail immediately. After 10 retries I'd like all failures to propagate to the caller.
I've been unable to code the flow control in a satisfying way. Here's one example of the behavior (but not the style!) I want:
def run():
max_retries = 10
for retry_index in range(max_retries):
try:
result = run_operation()
except OneTypeOfError:
if retry_index < max_retries - 1:
continue
else:
raise
if result.another_type_of_error:
if retry_index < max_retries - 1:
continue
else:
raise AnotherTypeOfError()
try:
result.do_a_followup_operation()
except AThirdTypeOfError:
if retry_index < max_retries - 1:
continue
else:
raise
return result
raise Exception("We don't expect to end up here")
At first I thought I could just refactor this so the retry logic is separate from the error handling logic. The problem is that if, for example, OneTypeOfError is raised by result.do_a_followup_operation(), I don't want to retry at all in that case. I only want to retry in the specific circumstances coded above.
So I thought perhaps I could refactor this into a function which returns the result, a raised Exception (if any), and a flag indicating whether it's a retryable exception. Somehow that felt less elegant than the above to me.
I'm wondering if there are any flow control patterns in Python which might help here.

You can use a specific exception class and recursion to be a little more dry. Sth along these lines:
class Retry(Exception):
def __init__(self, e):
super(Exception, self).__init__()
self.e = e
def run(max_retries=10, exc=None):
if max_retries <= 0:
raise exc or Exception('Whatever')
try:
try:
result = run_operation()
except OneTypeOfError as e:
raise Retry(e)
if result.another_type_of_error:
raise Retry(AnotherTypeOfError())
try:
result.do_a_followup_operation()
except AThirdTypeOfError as e:
raise Retry(e)
except Retry as r:
return run(max_retries=max_retries-1, exc=r.e)
else:
return result
An iterative solution with a given number of iterations seems semantically questionable. After all, you want the whole thing to succeed and a retry is a fallback. And having run out of retries sounds like a base case to me.

If I understand this correctly, you could do it as follows by changing two things:
Counting tries_left down from 10 instead of retry_index up from 0 reads more naturally and lets you exploit that positive numbers are truthy.
If you changed (or wrapped) run_operation() such that it would already raise AnotherTypeOfError if result.another_error is true, you could combine the first two except blocks.
The code can optionally be made slightly more dense by omitting the else after raise (or after continue if you choose to test for if tries_left instead) – the control flow is diverted at that point anyway –, and by putting a simple statement on the same line as a bare if without else.
for tries_left in range(10, -1, -1):
try:
result = run_operation()
except OneTypeOfError, AnotherTypeOfError:
if not tries_left: raise
continue
try:
result.do_a_followup_operation()
except AThirdTypeOfError:
if not tries_left: raise
continue
return result

Edit: Ah I didn't realize that your code indicated you didn't use multiple except blocks, as pointed out by JETM
Here's your quick primer on ExceptionHandling:
try:
# do something
except OneTypeOfError as e:
# handle one type of error one way
print(e) # if you want to see the Exception raised but not raise it
except AnotherTypeOfError as e:
# handle another type of error another way
raise e('your own message')
except (ThirdTypeOfError, FourthTypeOfError) as e:
# handle error types 3 & 4 the same way
print(e) # if you want to see the Exception raised but not raise it
except: # DONT DO THIS!!!
'''
Catches all and any exceptions raised.
DONT DO THIS. Makes it hard to figure out what goes wrong.
'''
else:
# if the try block succeeds and no error is raisedm then do this.
finally:
'''
Whether the try block succeeds or fails and one of the except blocks is activated.
Once all those are done with, finally run this block.
This works even if your program crashed and so is great for cleaning up for example.
'''
I did this once a long time ago, and recursively called the same function in one kind of exception but not another. I also passed the retry index and max_retries variable to the function, which meant adding those as parameters.
The other way would be to place the entire thing in a for loop of max_retries and add a break in all except blocks for the exceptions where you don't want a retry.
Finally, instead of a for loop, you can put the entire thing in a while loop, insert an increment condition in except block for one type of exception and make the while condition false in except block for other exceptions.

Related

try-except block: analogue for 'else' if exception was raised

I have this kind of code:
try:
return make_success_result()
except FirstException:
handle_first_exception()
return make_error_result()
except SecondException:
handle_second_exception()
return make_error_result()
And I'm wondering is there any way I can achieve this:
try:
# do something
except Error1:
# do Error1 specific handling
except Error2:
# do Error2 specific handling
else:
# do this if there was no exception
????:
# ALSO do this if there was ANY of the listed exceptions (e.g. some common error handling)
So the code is executed in one of following sequences:
try > else > finally
try > except > ???? > finally
EDIT: my point here is that ???? block should execute right after ANY of the except blocks, meaning that it's an addition to error handling, not a substitution.
What I would do in that case is to set a boolean when you get an exception, like so:
got_exception = False
try:
# do something
except Error1:
# do Error1 specific handling
got_exception = True
except Error2:
# do Error2 specific handling
got_exception = True
else:
# If there was no exception
finally:
if got_exception:
# ALSO do this if there was ANY exception (e.g. some common error handling)
This should fit your needs, which is IMO the cleanest way of combining all of the solutions which have been presented into the most readable code structure that's going to be the easiest to debug.
You can actually do this:
try:
print 'try'
# 1/0
# {}[1]
# {}.a
except AttributeError, KeyError: # only handle these exceptions..
try:
raise # re-raise the exception so we can add a finally-clause executing iff there was an exception.
except AttributeError:
print 'attrerr'
# raise ... # any raises here will also execute 'common'
except KeyError:
print 'keyerror'
finally: # update 0: you wanted the common code after the exceptions..
print "common"
else:
print 'no exception'
but it is horrid and I would not suggest that you do without copious amounts of comments describing why..
UPDATE: you don't need to catch anything but the interesting exceptions in the inner try-block. Code updated.
UPDATE2: per OP's clarification, common should just be executed when an interesting exception is thrown. Code updated. #MattTaylor's version is definitely the way to go ;-)
Yes, exception handling in python includes both an else and a finally clause. You can do this:
try:
# do something
except Error1:
# do Error1 specific handling
except Error2:
# do Error2 specific handling
else:
# do this if there was no exception
finally:
# Do this in any case!
The python docs mention these blocks, even though it doesn't show the full example you need.
EDIT:
I see that you do not ask specifically for the clean-up in the general case. Python docs put it this way:
The try statement has another optional clause which is intended to define clean-up actions that must be executed under all circumstances.
Note that the finally will run whether there was an exception or not. Combined with the else block, you should still be able to do what you want.
You could trap all errors and check for its type in the error handling code like this:
try:
# do something
except Exception as e:
if isinstance(e, Error1):
# do Error1 specific handling
elif isinstance(e, Error2):
# do Error2 specific handling
else:
# do non-Error1/Error2 handling
# ALSO do this if there was ANY exception (e.g. some common error handling)
else:
# do this if there was no exception

How to return a value and raise an Exception

I have two objectives with this try/except statement.
It needs to return a value of 1 if no problems occurred, or 0 if any problems occurred.
It needs to raise an exception and end the script.
I have the return value working. I also have the SystemExit() working. But together, they aren't working.
My Python Script (that's relevant):
except IOError:
value_to_return = 0
return value_to_return
raise SystemExit("FOOBAR")
With this, it ignores the raise SystemExit("FOOBAR") line completely. How do I go about getting a returned value and still raise SystemExit("FOOBAR")? This may be elementary to some, but I'm actually having quite a bit of difficulty with it.
Returning and raising are mutually exclusive.
Raising SystemExit will end the script. A few cleanup routines get to run, and if the caller really, really wants to, they can catch the SystemExit and cancel it, but mostly, you can think of it as stopping execution right there. The caller will never get a chance to see a return value or do anything meaningful with it.
Returning means you want the script to continue. Continuing might mean having the caller raise SystemExit, or it might mean ignoring the error, or it might mean something else. Whatever it means is up to you, as you're the one writing the code.
Finally, are you sure you should be handling this error at all? Catching an exception only to turn it into a system shutdown may not be the most useful behavior. It's not a user-friendly way to deal with problems, and it hides all the useful debugging information you'd get from a stack trace.
You can raise an error with a 'returning_value' argument to be used after the calling.
Another pythonic answer to your problem could be to make use of the error arguments in the raise and then, in your call manage the error to get the value, convert it from string and get your 'return-ish'.
def your_f():
try:
some_io_thingy_ok()
return 1
except IOError:
raise SystemExit("FOOBAR", 0)
try:
my_returning_value = your_f()
except SystemExit as err:
my_returning_value = err.args[1]
print(my_returning_value)
From Python 3 docs :
When an exception occurs, it may have an associated value, also known
as the exception’s argument. The presence and type of the argument
depend on the exception type.
The except clause may specify a variable after the exception name. The
variable is bound to an exception instance with the arguments stored
in instance.args. For convenience, the exception instance defines
str() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before
raising it and add any attributes to it as desired.
To exit a script and return an exit status, use sys.exit():
import sys
sys.exit(value_to_return)
I think what you may be looking for is something more like this:
def some_function():
# this function should probably do some stuff, then return 1 if
# it was successful or 0 otherwise.
pass
def calling_function():
a = some_function()
if a == 1:
raise SystemExit('Get the heck outta here!')
else:
# Everything worked!
pass
You can't "raise" and "return" in the same time, so you have to add a special variable to the return value (e.g: in tuple) in case of error.
E.g:
I have a function (named "func") which counts something and I need the (partial) result even if an exception happened during the counting. In my example I will use KeyboardInterrupt exception (the user pressed CTRL-C).
Without exception handling in the function (it's wrong, in case of any exception the function doesn't give back anything):
def func():
s=0
for i in range(10):
s=s+1
time.sleep(0.1)
return s
x=0
try:
for i in range(10):
s=func()
x=x+s
except KeyboardInterrupt:
print(x)
else:
print(x)
And now I introduce a boolean return value (in a tuple, next to the original return value) to indicate if an exception happened. Because in the function I handle only the KeyboardInterrupt exception, I can be sure that's happened, so I can raise the same where I called the function:
def func():
try:
s=0
for i in range(10):
s=s+1
time.sleep(0.1)
except KeyboardInterrupt: # <- the trick is here
return s, True # <- the trick is here
return s, False # <- the trick is here
x=0
try:
for i in range(10):
s,e=func()
x=x+s
if e: # <- and here
raise KeyboardInterrupt # <- and here
except KeyboardInterrupt:
print(x)
else:
print(x)
Note: my example is python3. The time module is used (in both code above), but I haven't import it just to make it shorter. If you want to really try it, put at the beginning:
import time
i was looking for an answer without using try, use 'finally' keyword like this.. if any one knows fill me in
here is an answer for your poblem
try:
9/0#sample error "don't know how to make IOError"
except ZeroDivisionError:
value_to_return = 0
raise SystemExit("FOOBAR")
finally:return value_to_return

Looping until a completely reliable exception

Right now I'm doing this:
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
I repeatedly call client.recv_into until it raises an exception. Now, I know eventually, without a doubt, it will raise an exception...
Is there a cleaner way to do this? Is there some sort of loop-until-exception construct or a common way to format this?
There are two ways to do this:
Like you did
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
pass
or
while True:
try:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
break
Personally, I would prefer the second solution as the break keyword makes clear what is happening in case of the exception.
Secondly, you should only catch the exception you're awaiting (in my example YourException). Otherwise IOError, KeyboardInterrupt, SystemExit etc. will also be caught, hiding "real errors" and potentially blocking your program from exiting properly.
That appears to be the (pythonic) way to do it. Indeed, the Python itertools page gives the following recipe for iter_except (calls a function func until the desired exception occurs):
def iter_except(func, exception, first=None):
try:
if first is not None:
yield first()
while 1:
yield func()
except exception:
pass
which is almost exactly what you're done here (although you probably do want to add a except [ExceptionName] line in your try loop, as Nils mentioned).

Why do we need the "finally" clause in Python?

I am not sure why we need finally in try...except...finally statements. In my opinion, this code block
try:
run_code1()
except TypeError:
run_code2()
other_code()
is the same with this one using finally:
try:
run_code1()
except TypeError:
run_code2()
finally:
other_code()
Am I missing something?
It makes a difference if you return early:
try:
run_code1()
except TypeError:
run_code2()
return None # The finally block is run before the method returns
finally:
other_code()
Compare to this:
try:
run_code1()
except TypeError:
run_code2()
return None
other_code() # This doesn't get run if there's an exception.
Other situations that can cause differences:
If an exception is thrown inside the except block.
If an exception is thrown in run_code1() but it's not a TypeError.
Other control flow statements such as continue and break statements.
You can use finally to make sure files or resources are closed or released regardless of whether an exception occurs, even if you don't catch the exception. (Or if you don't catch that specific exception.)
myfile = open("test.txt", "w")
try:
myfile.write("the Answer is: ")
myfile.write(42) # raises TypeError, which will be propagated to caller
finally:
myfile.close() # will be executed before TypeError is propagated
In this example you'd be better off using the with statement, but this kind of structure can be used for other kinds of resources.
A few years later, I wrote a blog post about an abuse of finally that readers may find amusing.
They are not equivalent. finally code is run no matter what else happens*.
It is useful for cleanup code that has to run.
*:
As Mark Byers commented, anything causes the process to terminate immediately also prevents the finally-code to run.
The latter could be an os._exit(). or powercut, but an infinite loop or other things also fall into that category.
To add to the other answers above, the finally clause executes no matter what whereas the else clause executes only if an exception was not raised.
For example, writing to a file with no exceptions will output the following:
file = open('test.txt', 'w')
try:
file.write("Testing.")
print("Writing to file.")
except IOError:
print("Could not write to file.")
else:
print("Write successful.")
finally:
file.close()
print("File closed.")
OUTPUT:
Writing to file.
Write successful.
File closed.
If there is an exception, the code will output the following, (note that a deliberate error is caused by keeping the file read-only.
file = open('test.txt', 'r')
try:
file.write("Testing.")
print("Writing to file.")
except IOError:
print("Could not write to file.")
else:
print("Write successful.")
finally:
file.close()
print("File closed.")
OUTPUT:
Could not write to file.
File closed.
We can see that the finally clause executes regardless of an exception. Hope this helps.
Here's a peice of code to clarify the difference:
...
try:
a/b
print('In try block')
except TypeError:
print('In except block')
finally:
print('In finally block')
print('Outside')
a, b = 0, 1
Output:
In try block
In finally block
Outside
(No errors, except block skipped.)
a, b = 1, 0
Output:
In finally block
Traceback (most recent call last):
a/b
ZeroDivisionError: division by zero
(No exception handling is specified for ZeroDivisionError and only the finally block is executed.)
a, b = 0, '1'
Output:
In except block
In finally block
Outside
(Exception is handled properly and the program is not interrupted.)
Note: If you have an except block to handle all types of errors, the finally block will be superfluous.
As explained in the documentation, the finally clause is intended to define clean-up actions that must be executed under all circumstances.
If finally is present, it specifies a ‘cleanup’ handler. The try
clause is executed, including any except and else clauses. If an
exception occurs in any of the clauses and is not handled, the
exception is temporarily saved. The finally clause is executed. If
there is a saved exception it is re-raised at the end of the finally
clause.
An example:
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
As you can see, the finally clause is executed in any event. The TypeError raised by dividing two strings is not handled by the except clause and therefore re-raised after the finally clause has been executed.
In real world applications, the finally clause is useful for releasing external resources (such as files or network connections), regardless of whether the use of the resource was successful.
The code blocks are not equivalent. The finally clause will also be run if run_code1() throws an exception other than TypeError, or if run_code2() throws an exception, while other_code() in the first version wouldn't be run in these cases.
In your first example, what happens if run_code1() raises an exception that is not TypeError? ... other_code() will not be executed.
Compare that with the finally: version: other_code() is guaranteed to be executed regardless of any exception being raised.
Using delphi professionally for some years taught me to safeguard my cleanup routines using finally. Delphi pretty much enforces the use of finally to clean up any resources created before the try block, lest you cause a memory leak. This is also how Java, Python and Ruby works.
resource = create_resource
try:
use resource
finally:
resource.cleanup
and resource will be cleaned up regardless of what you do between try and finally. Also, it won't be cleaned up if execution never reaches the try block. (i.e. create_resource itself throws an exception) It makes your code "exception safe".
As to why you actually need a finally block, not all languages do. In C++ where you have automatically called destructors which enforce cleanup when an exception unrolls the stack. I think this is a step up in the direction of cleaner code compared to try...finally languages.
{
type object1;
smart_pointer<type> object1(new type());
} // destructors are automagically called here in LIFO order so no finally required.
Finally can also be used when you want to run "optional" code before running the code for your main work and that optional code may fail for various reasons.
In the following example, we don't know precisely what kind of exceptions store_some_debug_info might throw.
We could run:
try:
store_some_debug_info()
except Exception:
pass
do_something_really_important()
But, most linters will complain about catching too vague of an exception. Also, since we're choosing to just pass for errors, the except block doesn't really add value.
try:
store_some_debug_info()
finally:
do_something_really_important()
The above code has the same effect as the 1st block of code but is more concise.
finally is for defining "clean up actions". The finally clause is executed in any event before leaving the try statement, whether an exception (even if you do not handle it) has occurred or not.
I second #Byers's example.
Perfect example is as below:
try:
#x = Hello + 20
x = 10 + 20
except:
print 'I am in except block'
x = 20 + 30
else:
print 'I am in else block'
x += 1
finally:
print 'Finally x = %s' %(x)
A try block has just one mandatory clause: The try statement.
The except, else and finally clauses are optional and based on user preference.
finally:
Before Python leaves the try statement, it will run the code in the finally block under any conditions, even if it's ending the program. E.g., if Python ran into an error while running code in the except or else block, the finally block will still be executed before stopping the program.
Try running this code first without a finally block,
1 / 0 causes a divide by zero error.
try:
1 / 0
print(1)
except Exception as e:
1 / 0
print(e)
Then try running this code,
try:
1 / 0
print(1)
except Exception as e:
1 / 0
print(e)
finally:
print('finally')
For the first case you don't have a finally block,
So when an error occurs in the except block the program execution halts and you cannot execute anything after the except block.
But for the second case,
The error occurs but before the program halts python executes the finally block first and then causes the program to halt.
Thats is why you use finally and do stuff that is really important.
Run these Python3 codes to watch the need of finally:
CASE1:
count = 0
while True:
count += 1
if count > 3:
break
else:
try:
x = int(input("Enter your lock number here: "))
if x == 586:
print("Your lock has unlocked :)")
break
else:
print("Try again!!")
continue
except:
print("Invalid entry!!")
finally:
print("Your Attempts: {}".format(count))
CASE2:
count = 0
while True:
count += 1
if count > 3:
break
else:
try:
x = int(input("Enter your lock number here: "))
if x == 586:
print("Your lock has unlocked :)")
break
else:
print("Try again!!")
continue
except:
print("Invalid entry!!")
print("Your Attempts: {}".format(count))
Try the following inputs each time:
random integers
correct code which is 586(Try this and you will get your answer)
random strings
** At a very early stage of learning Python.
I was trying to run a code where i wanted to read excel sheets. Issue was, if there is a file which has no sheet named say : SheetSum I am not able to move it to error location!! Code i wrote was:
def read_file(data_file):
# data_file = '\rr\ex.xlsx'
sheets = {}
try:
print("Reading file: "+data_file)
sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
except Exception as excpt:
print("Exception occurred", exc_info=True)
return sheets
read_file(file)
shutil.move( file, dirpath +'\\processed_files')
Giving Error :
[WinError 32] The process cannot access the file because it is being
used by another process
I had to add full try except with finally block and tell finally i need to close the file in any case like:
def read_file(data_file):
# data_file = '\rr\ex.xlsx'
sheets = {}
sheets_file = None
try:
print("Reading file: "+data_file)
sheets_file = open(data_file,'rb')
sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
except Exception as excpt:
print("Exception occurred", exc_info=True)
finally:
if sheets_file:
sheets_file.close()
return sheets
read_file(file)
shutil.move( file, dirpath +'\\processed_files')
Otherwise, file still remains open is the background.
If finally is present, it specifies a cleanup handler. The try
clause is executed, including any except and else clauses. If an
exception occurs in any of the clauses and is not handled, the
exception is temporarily saved. The finally clause is executed. If
there is a saved exception it is re-raised at the end of the finally
clause. If the finally clause raises another exception, the saved
exception is set as the context of the new exception.
..More Here
Just to enable Abhijit Sahu's comment on this answer to be seen much better and with syntax highlighting:
Like this, you can observe what happens with which code block when:
try:
x = Hello + 20
x = 10 + 20
except:
print 'I am in except block'
x = 20 + 30
else:
print 'I am in else block'
x += 1
finally:
print 'Finally x = %s' %(x)
Here you can see how try, except, else, and finally work together. Actually, since your code doesn't have 'else' then what you claimed is right. I.e., There is no difference between the two statements that you wrote. But if 'else' is used somewhere, then 'finally' makes a difference

Exception handling - run function again by nesting or looping?

I've got a function that often throws an exception (SSH over 3g).
I'd like to keep trying to run function() every 10 seconds until it succeeds (doesn't throw an exception).
As I see it, there are two options:
Nesting:
def nestwrapper():
try:
output = function()
except SSHException as e:
# Try again
sleep(10)
return nestwrapper()
return output
Looping: (updated)
It's been pointed out that the previous looping code was pretty unnecessary.
def loopwrapper():
while True:
try:
return function()
except SSHException as e:
sleep(10)
Is there a preferred method of doing this?
Is there an issue with nesting and the exception stack?
I would find a loop to be cleaner and more efficient here. If this is an automation job, the recursive method could hit python recursion limit (default is 1000 iirc, can check with sys.getrecursionlimit()).
Don't use status is False for your expression, because this is an identity comparison. Use while not status.
I would probably implement it slightly differently too, because I don't see any need for the two different functions here:
def function_with_retries():
while True:
try:
output = function()
except SSHException:
sleep(10)
else:
return output
I'm not sure it makes a heck of a lot of sense to specially wrap the function call twice. the exception is probably reasonable, and you're going to the extra step of retrying on that particular exception. What I mean is that the try/except is rather tightly involved with the retrying loop.
This is the way I normally do this:
def retry_something():
while True:
try:
return something()
except SomeSpecialError:
sleep(10)
The while True: is really exactly what you're up to, you're going to loop forever, or rather, until you actually manage to something(), and then return. There's no further need for a boolean flag of success, that's indicated by the normal case of the return statement (which politely escapes the loop).
Keep it simple.
function looper(f):
while 1:
try:
return f()
except SSHException, e:
sleep(10)
output = looper(<function to call>)

Categories

Resources