When raised, SystemExit causes the interpreter to exit, unless the exception is handled.
try:
raise SystemExit()
except SystemExit:
print('not today')
# Continue flow...
I want to mimic that behavior and write my own exception which executes some code only if the exception isn't caught.
Obviously this wouldn't work:
class MyFatalError(Exception):
def __init__(self):
import sys
sys.exit()
try:
raise MyFatalError()
except MyFatalError:
print('never gets here')
(...because an __init__ executes on instantiation, regardless of whether the error is handled.)
Is there a (straightforward and pythonic) way to make an exception behave differently when being handled?
You should not be thinking of SystemExit as a normal exception. All higher-level languages have a function to shut down the process immediately, and that would be sys._exit() in python, but there is also sys.exit() that does kinda the same, but allows your finally blocks to run first, and is implemented as an exception.
What you want should be done by having a try - except block at the lowest level of your application. The behaviour should be in the handler code, not in the exception.
Related
I have an application that looks like this:
while True:
try:
self.try_to_read_usb_device()
break
except:
time.sleep(1)
I also have an SIGALRM handler which is supposed to exit the program in case it got stuck somewhere:
def alarm_signal_handler(signal, frame):
# Something went wrong
exit(1)
However the exit(1) just get caught by the try/except and gets discarded as this is what that specific except does.
This is quite unexpected to me.
In the complete application there will be a lot of try/except and I don't see myself adding
except SystemExit:
exit(1)
or something for all of them.
Any idea how I should handle that use-case?
The dirty way
You can use os._exit instead of sys.exit.
Note that this has obvious drawbacks exactly because it won't go through an exception:
Exit the process with status n, without calling cleanup handlers, flushing stdio buffers, etc.
The proper way
I'd recommend to instead change your exception handling to catch only things inheriting from Exception, because SystemExit doesn't inherit from Exception for precisely this reason, so it won't be caught accidentally:
except Exception:
See also the SystemExit documentation:
This exception is raised by the sys.exit() function. It inherits from BaseException instead of Exception so that it is not accidentally caught by code that catches Exception. This allows the exception to properly propagate up and cause the interpreter to exit.
This also applies to KeyboardInterrupt by the way - Ctrl+C will be caught by except: but not by except Exception:.
It's illustrated pretty well in the exception hierarchy diagram in the Python docs, which you can find here.
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.
I am working on a simple automation script in Python, which could throw exceptions in various spots. In each of them I would like to log a specific message and exit the program. In order to do that, I raise SystemExit after catching the exception and handling it (performing specific logging operations and such).
In the top-level calling of main, I do the following:
if __name__ == "__main__":
try:
main()
except SystemExit: # handled exception
sys.exit(1)
except: # any unhandled exception
logging.error('Unexpected error: ', exc_info=True)
sys.exit(2)
However, using a bare except is something frowned upon. Is using an "exception tree" where I use a bare except to specify "anything but the exception that I've handled" a nonstandard way? Is there a better way to achieve this? I would still like to log these unhandled exceptions, even if they were not handled.
Edit: SystemExit is raised to note that an exception has been handled - no matter what the exception is in my case, I always want to stop running the scripts as any failure should result in an absolute failure.
The main reason I'm asking this is that PEP8 seems to consider using a bare except as an error, and even though I could use except BaseException, it should be just a syntactic difference. Is one way more standard than the other or is there another standard route of achieving this?
Bare exceptions trap things you do not want to trap, such as GeneratorExit. Do it this way:
except Exception as details:
logging.error('Unexpected error: {0}'.format(details))
The main issue with a bare except is that it can catch things like SystemExit and KeyboardInterrupt which are not standard 'code' errors and shouldn't usually be handled in the same way as an exception generated by your code. Using the Exception class doesn't cover those cases as they do not inherit from it, so it is more than a syntax difference.
https://docs.python.org/2/howto/doanddont.html#except
https://docs.python.org/3.1/howto/doanddont.html#except
If you want to handle those specific cases, then it is better to do so explicitly as you have done for SystemExit.
This worked for me:
try:
<code>
raise Exception("my error")
except Exception as e:
raise e
If my error occurs then the error message "my errror" is seen. If an unknown exception occurs then it displays the default exception handler's text. In either case an exception is raised and the script is halted.
I am using thread to invoke a function which runs as :
def fillIQ(ipno):
global inp_width
global no_exit
try:
while 1 :
if no_exit==1:
sys.exit() # <exit line>
tags=[]
for i in range(ipno):
yn=random.randint(0,1)
if yn==1:
voqno=random.randint(0,ipno-1)
if inpQ[i][voqno]<10:
inpQ[i][voqno]+=1
tag="iq"
tags.append(tag)
d.update()
time.sleep(2)
d.delete("iq")
drawIQ(ipno)
except BaseException ,e:
print "fillIQ > "+e
I am changing the value of no_exit in the main function. But once I change it the thread is not getting exit. Because next time I create a thread for another instance with different inputs( its a GUI program. for one input i execute the thread and later, change input and execute it again) the odler thread appears to run.
To exit a thread you must either return from it or raise SystemExit(). Returning actually raises a SystemExit exception in the context of a thread. But you are catching that exception and not re-raising it.
>>> isinstance(SystemExit(1), BaseException)
True
There's your problem. Either catch SystemExit exceptions separately and re-raise it explicitly, or be more selective with your general catching, or re-raise everything you catch. For brevity, I give an example of the former
except SystemExit:
raise
except BaseException:
print "fillIQ > ", e
Also, note that it's , e and not +e since e is not a string. There were a couple of other issues I encountered just trying to get your thread to run, but I'm sure you'll figure them out quickly enough, mostly missing imports and other values that are not listed in your example.
Could somebody explain to me the following. Lets take a look at the code:
if __name__ == '__main__':
try:
while 1:
x = 2+2
except KeyboardInterrupt:
print('yo')
If I run this, wait for a while, then press Ctrl+C, an exception will be processed and the message yo will be printed.
If we change the code to catch a broad exception like this:
if __name__ == '__main__':
try:
while 1:
x = 2+2
except Exception, e:
print('yo')
print(e)
Run it, wait for a while, press Ctrl+C, the KeyboardInterrupt exception will not be caught.
According to Python documentation:
Python installs a small number of signal handlers by default: SIGPIPE is ignored (so write errors on pipes and sockets can be reported as ordinary Python exceptions) and SIGINT is translated into a KeyboardInterrupt exception. All of these can be overridden.
So, why in the second case is this exception not caught or even raised?
You cannot catch KeyboardInterrupt by catching Exception because the former inherits from BaseException only. You can read about this in the docs:
exception KeyboardInterrupt
Raised when the user hits the interrupt key (normally Control-C or Delete). During execution, a check for interrupts is made regularly.
Interrupts typed when a built-in function input() or raw_input() is
waiting for input also raise this exception. The exception inherits
from BaseException so as to not be accidentally caught by code that
catches Exception and thus prevent the interpreter from exiting. (Emphasis mine)
This means that you would have to do:
except BaseException, e:
But that is considered a bad practice. It would be better to just catch KeyboardInterrupt itself like in your first example.