I'm trying to understand how Python handles exceptions within exception handling. For example, consider the following:
try:
try:
1/0
finally:
raise Exception("Exception!")
except Exception as e:
print(e)
My understanding is that both exceptions thrown by this code (both the ZeroDivisionError and the generic exception thrown in the finally block) should be "handled" by the outside except block...but how does Python decide which one to assign to e? Running the code on my machine, it seems that Python chooses to assign the "most recent" exception (the one thrown in the finally block) to e.
Is this generally true? Also, in a case like this where multiple exceptions might be thrown inside of error handling that are all handled by an outer except block, is there a way for the outer except block to step through each of the errors separately?
The Python docs have this:
If an exception occurs during execution of the try clause, the
exception may be handled by an except clause. If the exception is not
handled by an except clause, the exception is re-raised after the
finally clause has been executed.
So in your example, you don't catch the inner exception, this causes the finally block to execute (before re-raising the original exception). The exception in finally kicks it to the outside block before there is a chance to re-raise the original exception. The outside block never sees the divide-by-zero exception.
This is similar to returning from a function in finally:
def ex_test():
try:
try:
1/0
finally:
return "finally"
except Exception as e:
print(e) # never gets here
ex_test()
# only prints "finally"
# never re-raises the exception
You can get some information about the original exception. From the docs:
When raising (or re-raising) an exception in an except or finally
clause context is automatically set to the last exception caught;
if the new exception is not handled the traceback that is eventually
displayed will include the originating exception(s) and the final
exception.
So:
try:
try:
1/0
finally:
raise Exception("Exception!")
except Exception as e:
print(e.__context__)
# prints: "division by zero"
Related
From a list of exception treatment (but without covering each possible exception) how can I end (break) before reaching the generic treatment?
See below the point where I would like to "break" and just return from function or continue over the generic treatment:
try:
# do_f
except (ex1,ex2,ex3) as e:
# treat exception
except special_exception as e:
# treat this one
# do not continue to generic treatment/break <---
except Exception as e:
# generic treatment
In a try statement, only the first matching except clause will be executed.
All other except clauses will not be executed and you don't need to "break".
Reference: https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
I am wondering how to manage multiple exceptions in pyqt
I have a function 'encodeVideo()' that may trigger multiple exceptions.
def updateFilename(self):
try:
self.model.updateFilename(self.fileName)
except type_exc.PathIsEmpty as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
def updateOutput(self):
try:
self.model.updateOutput(self.encodeDialog.output)
except (type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
def encodeVideo(self):
self.updateFilename()
self.updateOutput()
In my case, it is likely to trigger errors both in updateFilname() and updateOutput. When this happens, a dialog will show up and report both errors. However, I seem to manage the exceptions in a wrong way. For example, when error in self.updateFilename() occurs, that doesn't stop my code from continuing the next code self.updateOutput().
You want to handle exceptions as a high as possible in your method call stack; this usually means that exceptions are handled in the UI where the first call was made, if inside any of your methods you need to do something if an exception occurs, you should catch and re-throw the exception, here are some examples:
In your code, the first method that is called from the UI is encodeVideo, therefore, you want to catch and handle your exceptions there:
def updateFilename(self):
self.model.updateFilename(self.fileName)
def updateOutput(self):
self.model.updateOutput(self.encodeDialog.output)
def encodeVideo(self):
try:
self.updateFilename()
self.updateOutput()
except (type_exc.PathIsEmpty, type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
Rethrow the exception
Let's imagine that if the call to updatedOutput fails, you want to do something specific, in this case, you can handle the exception in the inner method, but you should rethrow it again so it is handled by the calling method:
def updateOutput(self):
try:
self.model.updateOutput(self.encodeDialog.output)
except type_exc.FileAlreadyExists, e:
print("Do something")
raise type_exc.FileAlreadyExists(e)
def encodeVideo(self):
try:
self.updateFilename()
self.updateOutput()
except (type_exc.PathIsEmpty, type_exc.FileAlreadyExists, type_exc.PathNotExists) as e:
self.errorDialog.errorTypeChanged(e)
self.errorDialog.show()
This is basically a exception and error handling problem. And hence if there is error in any block then system takes it as an error handles or exception handles. Thus if first block of code gave error , then next block contain another handler exception so it is very simple that system treating it as a blocks of errors and exception.
try:
#error code
except Exception as e:
print 'error',e
raise miexp("malicious error")
#userdefined exception, miexp
finally:
print 'finally'
Why the output is in the following formats?
Output:
error
finally
malicious error
Actually I expected as:
error
malicious error
finally
Why so?
miexp("malicious error") isn't handled, therefore it will end the execution of the program. On the other hand, the finally block is guaranteed to be executed.
To ensure this Python executes the finally block before actually raising the exception. From the documentation:
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.
Is there a way to temporary stop catching some exception?
The point is that when you have more exceptions which are being catched in a code and you want to comment them because you want to see all the things printed when exception is raised (print exception is not sufficient), you have to comment try, except, code in except, finally, code in finally and change an indent of the code where the exception can be raised. This commenting is very time-consuming when you have to do it many times.
#try:
pel.check_one_destination()
#except Exception as e:
#pel.driver.save_screenshot('log.png')
#print e
You can just add raise keyword inside the except block and it will raise the caught exception back. Found it useful during testing.
Example -
try:
#code that leads to exception
except Exception as e:
#handle exception
raise #for testing what the exception was, and etc.
If I have a scenario where an exception is raised, caught, then raised again inside the except: block, is there a way to capture the initial stack frame from which it was raised?
The stack-trace that gets printed as python exits describes the place where the exception is raised a second time. Is there a way to raise the exception such that the stack frame that the exception was originally thrown is shown?
It's a common mistake to re-raise an exception by specifying the exception instance again, like this:
except Exception, ex:
# do something
raise ex
This strips the original traceback info and starts a new one. What you should do instead is this, without explicitly specifying the exception (i.e. use a "bare" raise):
except Exception, ex:
# do something
raise
This preserves all the original information in the stack trace. See this section in the docs for somewhat helpful background.