If exception retry in Python - python

How could I go about doing something like this
Try to do something.
If it works, great, continue with normal flow.
If it fails run a function and try again.
If it once again fails throw an exception and stop code.
I believe I would have to make use of try but I haven't quite come around yet to how to use it in this particular example.

It doesn't sound like you want to do a nested try-catch at all. Exceptions as control flow are a gnarly anti-pattern, and where you can avoid it, you should.
In this scenario, avoidance is easy. In the method that you describe, you want to be sure that a file exists before you do some operations on it. You also have a method to "correct" the path should it not. If both attempts fail, then you want to bail out.
With that into account, we would want to use os.path.isfile for this.
from os.path import isfile
def something(filepath):
# Don't mutate the parameter if you can help it.
p = filepath
if not isfile(p):
p = correct_path(p)
if not isfile(p):
raise Error("Cannot find file {} after correction to {}, aborting.".format(filepath, p))
with open(p, 'r') as f:
# Rest of file operations here

Try a nested try catch:
try:
do_something() #if this works, will skip rest and continue
except:
do_fix_function() #on error, do extra function
try:
do_something() #try again
except:
throw error() #if it fails this time, stop and throw an error
Note that if your do_fix_function() can also fail, you might want to put it inside the second try statement instead

This works for an arbitrary number of tries; I set it to two since that's what you want.
tries = 2
while True:
try:
step1()
except CatchThisException:
tries -= 1
if tries: # if tries != 0
step3()
continue # not necessary, just for clarity
else:
raise # step 4
else:
break # step 2

You can use retrying package to solve your retry attempt. Just write a block of code that keeps repeating on failure, until max retries is hit
For example:
import random
from retrying import retry
#retry
def do_something_unreliable():
if random.randint(0, 10) > 1:
raise IOError("Broken sauce, everything is hosed!!!111one")
else:
return "Awesome sauce!"
print do_something_unreliable()

Related

How can I end a 'try' loop in 'while' loop?

I'm in trouble about how to end a 'try' loop, which is occurred since I have the 'try', here is the code:
import time
class exe_loc:
mem = ''
lib = ''
main = ''
def wizard():
while True:
try:
temp_me = input('Please specify the full directory of the memory, usually it will be a folder called "mem"> ' )
if temp_me is True:
exe_loc.mem = temp_me
time.sleep(1)
else:
print('Error value! Please run this configurator again!')
sys.exit()
temp_lib = input('Please specify the full directory of the library, usually it will be a folder called "lib"> ')
if temp_lib is True:
exe_loc.lib = temp_lib
time.sleep(1)
else:
print('Invalid value! Please run this configurator again!')
sys.exit()
temp_main = input('Please specify the full main executable directory, usually it will be app main directory> ')
if temp_main is True:
exe_loc.main = temp_main
time.sleep(1)
I tried end it by using break, pass, and I even leaves it empty what I get is Unexpected EOF while parsing, I searched online and they said it is caused when the code blocks were not completed. Please show me if any of my code is wrong, thanks.
Btw, I'm using python 3 and I don't know how to be more specific for this question, kindly ask me if you did not understand. Sorry for my poor english.
EDIT: Solved by removing the try because I'm not using it, but I still wanna know how to end a try loop properly, thanks.
Your problem isn't the break, it's the overall, high-level shape of your try clause.
A try requires either an except or a finally block. You have neither, which means your try clause is never actually complete. So python keeps looking for the next bit until it reaches EOF (End Of File), at which point it complains.
The python docs explain in more detail, but basically you need either:
try:
do_stuff_here()
finally:
do_cleanup_here() # always runs, even if the above raises an exception
or
try:
do_stuff_here()
except SomeException:
handle_exception_here() # if do_stuff_here raised a SomeException
(You can also have both the except and finally.) If you don't need either the cleanup or the exception handling, that's even easier: just get rid of the try altogether, and have the block go directly under that while True.
Finally, as a terminology thing: try is not a loop. A loop is a bit of code that gets executed multiple times -- it loops. The try gets executed once. It's a "clause," not a "loop."
You have to also 'catch' the exception with the except statement, otherwise the try has no use.
So if you do something like:
try:
# some code here
except Exception:
# What to do if specified error is encountered
This way if anywhere in your try block an exception is raised it will not break your code, but it will be catched by your except.

python flow control for conditionally retrying an operation

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.

Exit Python program if data directory is empty

UPDATE: I think I may have just realized what I need to figure out re: the below, which is the correct error type to specify with the except clause (new to this, obviously)
Starting in a specified root directory, my program iterates through subdirectories, then files within those, identifying valid (.csv) data files and then aggregating and performing calculations on the data.
In cases where the root directory happens to be empty, can someone suggest a clean/graceful way to simply exit the program at the start without further processing?
I tried to adapt a suggestion I found here, but it didn't work as I expected:
Exit gracefully if file doesn't exist
That person's code was:
def main():
try:
file = open('file.txt', 'r')
except IOError:
print('There was an error opening the file!')
return
I gave the above a try, and it works for the particular case above. However, when I tried to adapt it as follows, it 'broke' and I got an "Index out of range error", instead dropping down to the except code.
dir = os.listdir(masterDirPath)
def main():
try:
item = dir[0]
except IOError:
print('The data area is empty.')
return
(Also/instead, I very much welcome suggestions for some completely other approach to the task overall)
To exit your program immediately you should use either exit() or quit(). Instead of throwing an error you could use the fact that many objects in Python are truthy; an empty list is False, and a list with one or more elements is True.
import os
dir_contents = os.listdir('.')
if dir_contents:
do_stuff
else:
print('Directory was empty. Exiting')
exit()
If you prefer explicitness to implicitness you could also check the length of your list using len(dir_contents) before indexing into it.
You might also want to avoid using dir in Python as a variable name as it will shadow the builtin function dir().
An empty list has no elements so you should catch IndexError instead of IOError.
def main():
try:
item = dir[0]
except IndexError:
print('The data area is empty.')
return
Why are you using an exception?
if not dir:
print('The data area is empty')
exit(0)
item = dir[0]
#!/usr/bin/env python
import os
path = 'path-to-your-dir'
if os.listdir(path) == []:
exit(0)
else:
print "Your other code goes here."
These are the exit commands and its definitions
exit(0) means a clean exit without any errors / problems
exit(1) means there was some issue / error / problem and that is why the program is exiting.
os._exit() for child processes
quit() the SystemExit exception behind the scenes.
sys.exit() kill the interpreter

syntaxError: 'continue' not properly in loop

I have been struggling with this error for a while now and there seems to be different opinions regarding why the interpreter complains about the 'continue'. So I would like to provide the erroneous code below.
import tweepy
import time
def writeHandlesToFile():
file = open("dataFile.txt","w")
try:
list = tweepy.Cursor(tweepy.api.followers,screen_name='someHandle',).items(100000)
print "cursor executed"
for item in list:
file.write(item.screen_name+"\n")
except tweepy.error.TweepError as e:
print "In the except method"
print e
time.sleep(3600)
continue
The reason I am particular on including the continue at the end is because I would like for the program to restart execution at the top from where it left off after the sleep in order to preserve the program state. I need the sleep in order to abide by the twitter api rate limits wherein the api only allows you to make a certain number of requests every hour.
So anyone who might see my mistake naive or otherwise please do point it out or please provide me with an alternative implementation without the use of the continue statement.
BTW I do not have tabs and spaces mixed as was suggested in another post.
Thank you for your help in advance.
continue is only allowed within a for or while loop. You can easily restructure your function to loop until a valid request.
def writeHandlesToFile():
while True:
with open("dataFile.txt","w") as f:
try:
lst = tweepy.Cursor(tweepy.api.followers,screen_name='someHandle',).items(100000)
print "cursor executed"
for item in lst:
f.write(item.screen_name+"\n")
break
except tweepy.error.TweepError as e:
print "In the except method"
print e
time.sleep(3600)
The problem might be in the way you are using continue
continue may only occur syntactically nested in a for or while loop,
but not nested in a function or class definition or finally statement
within that loop.6.1It continues with the next cycle of the nearest
enclosing loop.

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