69

I want to catch KeyboardInterrupt globally, and deal with it nicely. I don't want to encase my entire script in a huge try/except statement. Is there any way to do this?

Kye
  • 4,279
  • 3
  • 21
  • 49

5 Answers5

152

You could change sys.excepthook if you really don't want to use a try/except.

import sys
def my_except_hook(exctype, value, traceback):
    if exctype == KeyboardInterrupt:
        print "Handler code goes here"
    else:
        sys.__excepthook__(exctype, value, traceback)
sys.excepthook = my_except_hook
andrewrk
  • 30,272
  • 27
  • 92
  • 113
multipleinterfaces
  • 8,913
  • 4
  • 30
  • 34
25

If this is a script for execution on the command line, you can encapsulate your run-time logic in main(), call it in an if __name__ == '__main__' and wrap that.

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print 'Killed by user'
        sys.exit(0)
Rob Cowie
  • 22,259
  • 6
  • 62
  • 56
  • 4
    This won't handle uncaught exceptions in other threads, besides your main thread. As multipleinterfaces suggested, sys.excepthook is the way to go – Orlin Georgiev Aug 11 '16 at 13:42
  • @OrlinGeorgiev [This](http://stackoverflow.com/questions/1643327/sys-excepthook-and-threading) thread says otherwise. – akhan Dec 21 '16 at 00:47
  • This also won't help you when the exception is raised somewhere up the call stack and you have exception handlers between there & the top level. – Noah Jan 24 '22 at 17:47
21

You can also use signal like this:

import signal, time

def handler(signum, frame):
    print 'I just clicked on CTRL-C '

signal.signal(signal.SIGINT, handler)

print "waiting for 10 s"
time.sleep(10)

Output:

waiting for 10 s
^CI just clicked on CTRL-C

N.B: Don't mix the use of signal with threads.

mouad
  • 67,571
  • 18
  • 114
  • 106
  • 4
    Actually, catching the signal should be _the_ answer for this. – jsbueno Mar 19 '17 at 04:00
  • 2
    There is no problem with using signals in python threads. You just need to be aware of the fact, that you need to set the handler from the main threads and that it is always executed in the main thread. The default `KeyboardInterrupt` exception is also raised from a signal handler. – Bachsau Jun 09 '19 at 18:53
10

Does your script have a function you call to start it?

main()

then just do:

try:
    main()
except:
    ...

If you don't have a main but just a huge script that runs line-by-line, then you should put it in a main.

Claudiu
  • 224,032
  • 165
  • 485
  • 680
0

There's no other way to do this, apart from encasing your entire script in a main() function and surrounding that with a try..except block - which is pretty much the same:

def main():
    # Your script goes here
    pass

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        # cleanup code here
        pass
Tamás
  • 47,239
  • 12
  • 105
  • 124
  • 2
    The suggestion is valid, but it's incorrect that there is no other way, as demonstrated by other answers. – Teekin Nov 19 '19 at 20:37