I have a nested try-except-finally block where I run several functions in succession (assuming the previous ones worked). I have a condition that I check at the start (which essentially checks if the function has already been run that day) and if this statement is false, I want to jump straight to the finally.
I can do this by simply forcing an error to occur (i.e. writing x=1/0 but it seems there should be a better way to do this).
My code looks like this:
error = False
conditions = False
try:
# Do stuff here
if not condition:
# Here I want to go directly to finally
except Exception:
error = True
else:
try:
# Do stuff here
except Exception:
error = True
else:
try:
# Do stuff here
except Exception:
error = True
finally:
if error:
# Report that an error occurred
else:
# Report that everything went well
How about this?
To use MisterMiyagi's excellent wording in the comments to this answer, it inverts the logic to proceed only when the condition is met.
error = False
conditions = False
try:
# Do stuff here
except Exception:
error = True
else:
if condition: # The inverted condition moved here.
try:
# Do stuff here
except Exception:
error = True
else:
try:
# Do stuff here
except Exception:
error = True
finally:
if error:
# Report that an error occurred
else:
# Report that everything went well
Related
Let's say I have three functions that do different things but should react to a set of exceptions in the same way. One of them might look like:
def get_order_stat(self, Order_id):
status_returned = False
error_count = 0
while status_returned == False:
try:
stat_get = client.queryOrder(orderId=Order_id)
except MalformedRequest:
print('Order ID not yet findable, keep trying')
error_count += 1
time.sleep(int(1))
except InternalError:
print('Order check returned InternalError, keep trying')
error_count += 1
time.sleep(int(1))
except StatusUnknown:
print('Order check returned StatusUnknown, keep trying')
error_count += 1
time.sleep(int(1))
else:
status = stat_get['status']
status_returned = True
finally:
if error_count >= 10:
print('Error loop, give up')
break
return status
The vast majority of the code is the exception handling, and I'd like to avoid having to repeat it in every function that needs it. Is there a way to define something like an exception handling function containing the handling code? Ideally my function would end up effectively:
def get_order_stat(self, Order_id):
status_returned = False
while status_returned == False:
try:
stat_get = client.queryOrder(orderId=Order_id)
except:
handler_function()
else:
status = stat_get['status']
status_returned = True
return status
You practically already did it. Just define the handler_function() somewhere and it gets called when an Exception in the try block gets raised.
Maybe helpful: you can bind the Exception to a variable and use it for exception handling in the handler function:
except Exception as e:
handler_function(e)
Then you can for example do `print(e)̀ to give out the exception or do different handling for different exceptions in the function. Hope that helps!
You also can specify several exceptions in one line if you don't want to be general, but catch all specific exceptions with one statement:
except (ExceptionType1, ExceptionType2, ExceptionType3) as e:
handler_function(e)
I might write a decorator function for the exception handling; for instance using functool.wraps.
from functool import wraps
def retry(f):
#wraps(f)
def wrapper(*args, **kwargs):
error_count = 0
while error_count < 10:
try:
return f(*args, **kwargs)
except MalformedRequest:
print('Order ID not yet findable, keep trying')
except InternalError:
print('Order check returned InternalError, keep trying')
error_count += 1
time.sleep(int(1))
print('Error loop, give up')
return None
return wrapper
Then you can write a very simple API call function, and wrap it with the retry wrapper:
#retry
def get_order(order_id):
stat_get = client.queryOrder(orderId=order_id)
return stat_get['status']
In your original function, notice that you can move the contents of the try...else block into the main try block without affecting the logic (extracting the value from the query result won't raise one of the network-related exceptions), and then you can just return out of the try block instead of arranging to stop the loop. Then the contents of the try block are what I've broken out here into get_order(). I restructured the remaining loop a little and turned it into decorator form.
Below is my code in which my while loop is calling my function (function_called) to process the log file. When there is an exception when my function is processing each line then it should ignore rest of the code and transfer the control back to while loop to go to the next line.
So it should be:
--Fetch Line--
--If index error exception in line--
--ignore code--
--move to the next line
My code overview is mentioned below
def function_called():
try:
--Something is happening here--
except IndexError:
--If there is an exception then code needs to be ignored
and it should be transfered to the while loop to go to the next line--
f = open('/data/qantasflight/run/tomcat/logs/localhost_access_log.2016-03-31.txt', 'r')
while True:
line = ''
while len(line) == 0 or line[-1] != '\n':
tail = f.readline()
if tail == '':
continue
line = tail
print (line, end='')
# print (line)
sleep(0.1)
function_called(line)
You should catch the exception at the place where you want to handle it, i.e. in the while loop.
def function_called():
""" something, which might raise an index error """
while True:
""" do stuff """
try:
function_called(line)
except IndexError:
continue
If you want to do more explicit error handling in function_called, you can do that and just raise the exception again:
def function_called():
try:
""" something, which might raise an index error """
except IndexError:
""" more error handling """
raise
Check out the chapter about error handling in the official documentation.
I have piece of cake that is returning None in case exception is caught.
def getObject(ver):
av = None
try:
av = getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
return av
or this is better ?
def getObject(ver):
try:
av = getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
else:
return av
A shorter (more Pythonic, IMHO) equivalent to your first code snippet is as follows:
def getObject(ver):
try:
return getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
This works because python functions implicitly return None if control reaches the end of the function without encountering a return (as would be the case if your exception is caught).
First code is better.
Reasons:
Exception may occur before the return value is set to variable av.
In first code, None will be returned if exception occured. In second code, return value is undefined if exception occured.
None is better than undefined as return value.
try:
commands
try:
commands
try:
commands
try:
commands
except:
commands
return to final commands
except:
commands
return to final commands
except:
commands
return to final commands
except:
commands
final commands
Which instruction have I to write in place of return to final commands to make that any except returns to the top-level instructions following the outer try? And is it an acceptable structure?
Edit: Here is a toy example (I know I can do it using ifs, it's only an example; suppose you have to write it using try/except).
# calculate arcsin(log(sqrt(x)-4))
x = choose one yourself
try
x1=sqrt(x)
return to final message
try
x1=log(x1-4)
return to final message
try
x2=arcsin(x1)
return to final message
except
message="Impossible to calculate arcsin: maybe an argument outside [-1,+1]?"
except
message="Impossible to calculate logarithm: maybe a not positive argument?"
except
message="impossible to calculate square root: maybe a negative argument?"
final message:
print message
At the very least you should be able to reduce this structure to only 2 nested levels by reraising the exception to avoid the rest of the block:
# calculate arcsin(log(sqrt(x)-4))
x = ?
message = None
try:
try:
x1 = sqrt(x)
except Exception:
message = "can't take sqrt"
raise
try:
x1 = log(x1-4)
except Exception:
message = "can't compute log"
raise
try:
x2 = arcsin(x1)
except Exception:
message = "Can't calculate arcsin"
raise
except Exception:
print message
Really, this is not the way to do it, at least in this example. The problem is that you are trying to use exceptions like return error codes. What you should be doing is putting the error message into the exception. Also, normally the outer try/except would be in a higher level function:
def func():
try:
y = calculate_asin_log_sqrt(x)
# stuff that depends on y goes here
except MyError as e:
print e.message
# Stuff that happens whether or not the calculation fails goes here
def calculate_asin_log_sqrt(x):
try:
x1 = sqrt(x)
except Exception:
raise MyError("Can't calculate sqrt")
try:
x1 = log(x1-4)
except Exception:
raise MyError("Can't calculate log")
try:
x2 = arcsin(x1)
except Exception:
raise MyError("Can't calculate arcsin")
return x2
I'm trying to extract data from an xml file. A sample of my code is as follows:
from xml.dom import minidom
dom = minidom.parse("algorithms.xml")
...
parameter = dom.getElementsByTagName("Parameters")[0]
# loop over parameters
try:
while True:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except IndexError:
#reached end of available parameters
pass
#except AttributeError:
#parameter doesn't exist
#?
If all elements for each parameter exist, the code runs correctly. Unfortunately the data I am supplied often has missing entries in it, raising an AttributeError exception. If I simply pass on that error, then any elements that do exist but are retrieved later in the loop than when the exception occurred are skipped, which I don't want. I need some way to continue where the code left off and skip to the next line of code if this specific exception is raised.
The only way to work around this that I can think of would be to override the minidom's class methods and catch the exception there, but that seems far too messy and too much work to handle what should be a very simple and common problem. Is there some easier way to handle this that I am missing?
Instead of "an individual try-except block for every statement", why not abstract out that part?
def getParam(p, tagName, index, post=None):
post = post or lambda i: i
try:
return post(p.getElementsByTagName(tagname)[index])
except AttributeError:
print "informative message"
return None # will happen anyway, but why not be explicit?
then in the loop you could have things like:
parameter_id = getParam(parameter, "Parameter", m, lambda x: x.getAttribute("Id"))
parameter_name = getParam(parameter, "Name", m, lambda x: x.lastChild.data)
...
I think there are two parts to your question. First, you want the loop to continue after the first AttributeError. This you do by moving the try and except into the loop.
Something like this:
try:
while True:
try:
parameter_id = parameter.getElementsByTagName("Parameter")[m].getAttribute("Id")
parameter_name = parameter.getElementsByTagName("Name")[m].lastChild.data
...
parameter_default = parameter.getElementsByTagName("Default")[m].lastChild.data
print parameter_id
print parameter_default
m = m+1
except AttributeError:
print "parameter doesn't exist"
#?
except IndexError:
#reached end of available parameters
pass
The second part is more tricky. But it is nicely solved by the other answer.