Catch error when passing function as a parameter - python

I use a python script to update ~500 Google Sheets (using some wrappers on the google api) every morning, and find that I need to catch API errors all the time. I wanted to write a generic "try this function, catch any errors" function, and was able to pass in the function and its parameters successfully, but it doesn't seem to catch the errors. Only if I wrap the original function call in a try/catch does it actually handle the error. This works, but it's starting to feel like half of my code is try/catch protocol.
Should this be working? Is there something different I can do so I only need to write my "try protocols" once?
My attempt at a generic catch function:
def try_function(function, teacher_name="teacher", note="unknown when", tab=None):
check = True
count = 0
while check:
check = False
try:
function
except Exception as e:
if str(e).find("Quota exceeded")>-1:
print("API Quota exceeded " + note + ", waiting 100 seconds - " + teacher_name)
time.sleep(100)
check = True
count+=1
elif str(e).find("The service is currently unavailable")>-1:
print("API threw 'currently unavailable error' " + note + ", waiting 100 seconds - " + teacher_name)
time.sleep(100)
check = True
count+=1
elif str(e).find("Read timed out.")>-1:
print("API threw 'Read timed out.' error " + note + ", waiting 150 seconds - " + teacher_name)
time.sleep(150)
check = True
count+=1
elif str(e).find("Internal error encountered.")>-1:
print("API threw 'Internal error encountered' " + note + ", waiting 100 seconds - " + teacher_name)
time.sleep(100)
check = True
count+=1
else:
print("Non-quota error " + note + ", teacher may not have been updated, waiting 250s - " + teacher_name)
print(e)
time.sleep(250)
del spread
continue
if count > 4:
print("Tried this 5 times, ending try loop - " + teacher_name)
del spread
continue
Called like this: try_function(spread.df_to_sheet(parameters), "name", "note")
Do I have to wrap that last line in order to actually catch an error?

You need to actually call the function inside your try block, and the thing you pass in has to be an actual function (not the result of already having called a function). Simplifying down the exception handling into a generic retry for the sake of making the example simpler (note that in real life you would typically handle different types of exceptions by matching on the type of the exception rather than trying to parse its string representation), it would look like this:
def try_function(function, teacher_name="teacher", note="unknown when"):
count = 0
while True:
try:
function()
return
except Exception as e:
print(f"Error: {e} ({note}), waiting 100s and retrying...")
count += 1
time.sleep(100)
if count > 4:
print(f"Tried this 5 times, ending try loop - {teacher_name}")
return
try_function(lambda: spread.df_to_sheet(parameters), "name", "note")
Note that the lambda expression creates a function with no arguments that will invoke spread.df_to_sheet when it's called (which will happen inside your try block). This is the same as writing:
def to_sheet_fn():
spread.df_to_sheet(parameters)
try_function(to_sheet_fn, "name", "note")

Related

Function is not getting stopped at return at if conditions

I'm new to python, this below funtion is not getting stop at if condition where it needs to stop.
def check_pi_installation_status():
proc1cmd = "grep " + AppName
p1 = subprocess.Popen(['kubectl', 'get', 'pi', '-A'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(proc1cmd, shell=True, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p1.stdout.close()
stdout_list = p2.communicate()[0]
stdout_list = stdout_list.decode()
imagenamepi = stdout_list.split()
print("pi imagename ", imagenamepi)
if (imagenamepi[2] == 'Installed'):
print("Successfully Installed")
return
if (imagenamepi[2] == 'Failed'):
print(" Installation Failed")
return
if(imagenamepi[2] != 'Installed' and imagenamepi[2] != 'Failed'):
time.sleep(30)
# count = count + 1
# print("count", count)
write_eventslog()
check_pi_installation_status()
Below are the case:
when i started this function where its still not reached to 'Failed',it prints 'Installation Failed' on reaching state 'Failed' but it continues even when it reaches 'Failed' state.
I ran the function when state is already 'Failed' its works as expected (it prints 'Installation Failed' and come out.
Instead of using multiple if statements, why not use if, elif and else? The main difference here is that when you have multiple if statements, all of them will run no matter what, so change it to this:
if (imagenamepi[2] == 'Installed'):
print("Successfully Installed")
return
elif (imagenamepi[2] == 'Failed'):
print(" Installation Failed")
return
else:
time.sleep(30)
# count = count + 1
# print("count", count)
write_eventslog()
check_pi_installation_status()
Over here, we first used if, then elif. elif is a mixture between IF and ELSE, which basically tells the compiler: If the statement above did not run, check this one. You can have multiple elif's in one go. Lastly, we have else. Else tells the compiler: If none of the other statements ran, run this one. Unlike Elif, you can only have one Else condition.
The main difference between the two methods are that when you use If, Elif and Else, the entire statement is exited when a condition is run.
Although im not too sure why return doesnt exit out the function immediately, the use of elif and else would help you do the same thing. More than likely, it works on the fail state because none of the other two statements are applicable.

how to catch error(200)

I'm trying to get input from user, until he press ctrl-c. yet, I can't catch the
error, I think it has something to do with sklearn (I imported it for the rest of the code)
this is the code:
try:
while(True):
i+=1
put = input("\tEnter name of feature number " + str(i) +":\t")
features.append(put)
except KeyboardInterrupt:
print("\n\tFeatures Added!")
sleep(SLEEP)
return None
except:
exit("\nError has occurred, please come back later...")`
Fix your indentation as the following:
try:
while(True):
i+=1
put = input("\tEnter name of feature number " + str(i) +":\t")
features.append(put)
except KeyboardInterrupt:
print("\n\tFeatures Added!")
sleep(SLEEP)
return None
except:
exit("\nError has occurred, please come back later...")

Python: run "try" again after exception caught and worked out

Is there any way to enter try statement once again after catching the exception in the first try?
Now I'm using "while" and "if" statements and it is making the code messy.
Any ideas?
Will try to simplify it as possible, sorry that have no logic...
run = True
tryAgain = True
a=0
while run:
try:
2/a
except Exception:
if tryAgain:
tryAgain = False
a = 1
else:
run = False
You could try using a break statement in your try block:
while True:
try:
# try code
break # quit the loop if successful
except:
# error handling
Considering you are doing this in a while, then you can make use of continue to just continue back to the beginning of the while loop:
tryAgain = True
a=0
while True:
try:
2/a
break # if it worked then just break out of the loop
except Exception:
if tryAgain:
continue
else:
# whatever extra logic you nee to do here
I like using a for loop so that the trying and trying doesn't go on forever. Then the loop's else clause is a place to put the "I give up" code. Here is a general form that will support 'n' retries > 1:
a=0
num_tries = 5
for try_ in range(0,num_tries):
try:
2/a
except Exception:
print("failed, but we can try %d more time(s)" % (num_tries - try_ - 1))
if try_ == num_tries-2:
a = 1
else:
print("YESS!!! Success...")
break
else:
# if we got here, then never reached 'break' statement
print("tried and tried, but never succeeded")
prints:
failed, but we can try 4 more time(s)
failed, but we can try 3 more time(s)
failed, but we can try 2 more time(s)
failed, but we can try 1 more time(s)
YESS!!! Success...
I'm new to Python, so this may not be best practice. I returned to the try statement after triggering the exception by lumping everything into a single function, then recalling that function in the except statement.
def attempts():
while True:
try:
some code
break #breaks the loop when sucessful
except ValueError:
attempts() #recalls this function, starting back at the try statement
break
attempts()
Hope this addresses your question.

Reducing count value to repeat a loop cycle is not working. The for loop in python has an exception handler that has a continue statement

for i in range(0, 650):
s = ticket[i]
try:
response = resource.get(path='ticket/%s' % s[0]) # Get ticket data from RT server
except urllib2.URLError, e: # If connection fails
resource = RTResource(url, user, pwd, CookieAuthenticator) # Reconnect to RT server
count -= 1 # Count re-connection attempts
if count < 0:
print "Connection failed at ticket %s" % s[0]
print "Got %s tickets out of %s" % {i + 1, len(ticket) + 1}
wb.save(fname)
sys.exit(1)
print 'Trying again...'
i -= 1
continue
count = 10
...more code here...
The above code executes well but will skip an iteration when exception is thrown. I am trying to decreament the value of i and then continuing the loop so that when exception is thrown, the loop will repeat for same value of i. When a value of i is skipped I lose one ticket from RT server. How do I fix it?
You ... can't do that in python. You can't affect the value of the iterator - it's using it's own internal value for each step in the loop, not paying attention to your attempts to override. If you have to succeed for each iteration I use something like this:
while True:
# code here
if success:
break
And place that inside your for loop. Or better, extract a method to simplify readability, but that's another post.
(Besides the correct point raised by g.d.d.c. about the inability to decrement the loop counter the specific way you've gone, )this sort of stuff is exactly the motivation for finally. You should probably organize your code as follows:
try - the part that's supposed to run but might not
except - the part to do only if there was a problem
else (optional) - the part to do only if there wasn't a problem
finally - stuff to do in any case
An alternative to embedding a while loop in your for loop, as suggested by g.d.d.c, is to simply use a while loop instead of a for loop, like so:
i = 0
while i < 650:
s = ticket[i]
try:
response = resource.get(path='ticket/%s' % s[0]) # Get ticket data from RT server
except urllib2.URLError, e: # If connection fails
resource = RTResource(url, user, pwd, CookieAuthenticator) # Reconnect to RT server
count -= 1 # Count re-connection attempts
if count < 0:
print "Connection failed at ticket %s" % s[0]
print "Got %s tickets out of %s" % {i + 1, len(ticket) + 1}
wb.save(fname)
sys.exit(1)
print 'Trying again...'
continue
count = 10
i += 1
...more code here...

Re-enter try statement after an except

In my code below, it stops executing when I hit an exception. How can I get it to re-enter the try statement from where the exception left off? Maybe I am looking for a different way to tackle this without a try-except statement?
import requests
from requests import exceptions
contains_analyst = []
try:
for x in data:
r = requests.get(str(x), timeout=10, verify=False)
if "analyst" in r.text:
contains_analyst.append("Analyst")
print "Analyst # %s" % x
else:
contains_analyst.append("NOPERS")
print "Nopers"
except exceptions.RequestException:
contains_analyst.append("COULD NOT CONNECT")
You should put the try/except around only the part whose error you want to trap. In your example, it looks like you want something more like this:
for x in data:
try:
r = requests.get(str(x), timeout=10, verify=False)
except exceptions.RequestException:
contains_analyst.append("COULD NOT CONNECT")
else:
if "analyst" in r.text:
contains_analyst.append("Analyst")
print "Analyst # %s" % x
else:
contains_analyst.append("NOPERS")
print "Nopers"
Here I use the else clause of the try block to handle the case where no exception is raised (see documentation). In many cases, if you don't need to do anything else after the exception, you could just return at that point and put the following no-exception code in the main function body, reducing indentation a bit:
for x in data:
try:
r = requests.get(str(x), timeout=10, verify=False)
except exceptions.RequestException:
contains_analyst.append("COULD NOT CONNECT")
return contains_analyst
# execution reaches here if no exception
if "analyst" in r.text:
contains_analyst.append("Analyst")
print "Analyst # %s" % x
else:
contains_analyst.append("NOPERS")
print "Nopers"
Of course, whether it makes sense to return at that point depends on the surrounding context of your code.

Categories