KeyboardInterrupt not raised or caught in the case of broad Exception - python

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.

Related

exit(1) in signal handler just gets caught as SystemExit as there is nothing to it

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.

Python exceptions - catching all exceptions but the one expected

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.

How does an exception know if it's being handled?

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.

Python thread class - exiting threads

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.

I exit() a script but it does NOT exit

I try to exit a script but it doesn't exit.
Here is my code:
import sys
try:
...
print "I'm gonna die!"
sys.exit()
except:
...
print 'Still alive!'
And the results are:
I'm gonna die!
Still alive!
WHY?
You are catching the SystemExit exception with your blanket except clause. Don't do that. Always specify what exceptions you are expecting to avoid exactly these things.
If you really need to exit immediately, and want to skip normal exit processing, then you can use os._exit(status). But, as others have said, it's generally much better to exit using the normal path, and just not catch the SystemExit exception. And while we're on the topic, KeyboardInterrupt is another exception that you may not want to catch. (Using except Exception will not catch either SystemExit or KeyboardInterrupt.)
sys.exit() is implemented by raising the SystemExit exception, so cleanup actions specified by finally, except clauses of try statements are honored, and it is possible to intercept the exit attempt at an outer level.
In your example SystemExit is catched by the following except statement.

Categories