Not registered yet?
Register now! It is easy and done in 1 minute and gives you access to special discounts and much more!
Is there some way in Python to capture KeyboardInterrupt event without putting all the code inside a try-except statement?
I want to cleanly exit without trace if user presses Ctrl+C.
Yes, you can install an interrupt handler using the module signal, and wait forever using a threading.Event:
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
forever = threading.Event()
Note that there are some platform-specific issues with the signal module -- shouldn't affect this poster, but "On Windows, signal() can only be called with SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM. A ValueError will be raised in any other case."
Works well with threads, too. I hope you don't ever do while True: continue, though. (In that style, while True: pass would be neater, anyway.) That'd be very wasteful; try something like while True: time.sleep(60 * 60 * 24) (sleeping for a day at a time is an entirely arbitrary figure).
while True: continue
while True: pass
while True: time.sleep(60 * 60 * 24)
If you're using Chris Morgan's suggestion of using time (as you should), don't forget to import time :)
Calling sys.exit(0) triggers a SystemExit exception for me. You can make it work nicely if you use it in combination with this: stackoverflow.com/a/13723190/353094
You can use signal.pause() instead of sleeping repeatedly
This doesn't completely terminate a program if using multiprocessing
this does not work for me at all.
See also: stackoverflow.com/a/37446275/1959808
An alternative to setting your own signal handler is to use a context-manager to catch the exception and ignore it:
>>> class CleanExit(object):
... def __enter__(self):
... return self
... def __exit__(self, exc_type, exc_value, exc_tb):
... if exc_type is KeyboardInterrupt:
... return True
... return exc_type is None
>>> with CleanExit():
... input() #just to test it
This removes the try-except block while preserving some explicit mention of what is going on.
This also allows you to ignore the interrupt only in some portions of your code without having to set and reset again the signal handlers everytime.
nice, this solution does seem a bit more direct in expressing the purpose rather than dealing with signals.
Using multiprocessing library, I'm not sure on which object I should add those methods .. any clue ?
@Stéphane What do you mean? When dealing with multiprocessing you will have to deal with the signal in both the parent and child processes, since it might be triggered in both. It really depends on what you are doing and how your software will be used.
You can prevent printing a stack trace for KeyboardInterrupt, without try: ... except KeyboardInterrupt: pass (the most obvious and propably "best" solution, but you already know it and asked for something else) by replacing sys.excepthook. Something like
try: ... except KeyboardInterrupt: pass
def custom_excepthook(type, value, traceback):
if type is KeyboardInterrupt:
return # do nothing
sys.__excepthook__(type, value, traceback)
I want clean exit without trace if user press ctrl-c
catched ==> caught
This is not true at all. The KeyboardInterrupt exception is created during an interrupt handler. The default handler for SIGINT raises the KeyboardInterrupt so if you didn't want that behavior all you would have to do is provide a different signal handler for SIGINT. Your are correct in that exceptions can only be handled in a try/except however in this case you can keep the exception from ever being raised in the first place.
Yeah, I learned that about three minutes after posting, when kotlinski's answer rolled in ;)
I know this is an old question but I came here first and then discovered the atexit module. I do not know about its cross-platform track record or a full list of caveats yet, but so far it is exactly what I was looking for in trying to handle post-KeyboardInterrupt cleanup on Linux. Just wanted to throw in another way of approaching the problem.
I want to do post-exit clean-up in the context of Fabric operations, so wrapping everything in try/except wasn't an option for me either. I feel like atexit may be a good fit in such a situation, where your code is not at the top level of control flow.
atexit is very capable and readable out of the box, for example:
print "You are now leaving the Python sector."
You can also use it as a decorator (as of 2.6; this example is from the docs):
print "You are now leaving the Python sector."
If you wanted to make it specific to KeyboardInterrupt only, another person's answer to this question is probably better.
But note that the atexit module is only ~70 lines of code and it would not be hard to create a similar version that treats exceptions differently, for example passing the exceptions as arguments to the callback functions. (The limitation of atexit that would warrant a modified version: currently I can't conceive of a way for the exit-callback-functions to know about the exceptions; the atexit handler catches the exception, calls your callback(s), then re-raises that exception. But you could do this differently.)
For more info see:
atexit doesnt' work for KeyboardInterrupt (python 3.7)
If all you want is to not show the traceback, make your code like this:
## all your app logic here
## whatever your app does.
if __name__ == "__main__":
# do nothing here
(Yes, I know that this doesn't directly answer the question, but it's not really clear why needing a try/except block is objectionable -- maybe this makes it less annoying to the OP)
For some reason, this doesn't always work for me. signal.signal( signal.SIGINT, lambda s, f : sys.exit(0)) always does.
signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))
This doesn't always work with things such as pygtk which use threads. Sometimes ^C will just kill the current thread instead of the entire process, so the exception will only propagate through that thread.
There's another SO question specifically about Ctrl+C with pygtk: stackoverflow.com/questions/16410852/…