By Alex


2010-11-17 14:24:51 8 Comments

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.

5 comments

@Johan Kotlinski 2010-11-17 14:30:36

Yes, you can install an interrupt handler using the module signal, and wait forever using a threading.Event:

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()

@bgporter 2010-11-17 14:39:11

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."

@Chris Morgan 2011-10-06 12:04:30

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).

@Seaux 2013-05-30 17:37:04

If you're using Chris Morgan's suggestion of using time (as you should), don't forget to import time :)

@leetNightshade 2015-03-21 19:42:45

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

@Croad Langshan 2015-07-05 13:49:28

You can use signal.pause() instead of sleeping repeatedly

@Jake 2017-08-15 15:18:35

This doesn't completely terminate a program if using multiprocessing

@bgenchel 2018-02-19 22:04:17

this does not work for me at all.

@Ioannis Filippidis 2018-04-11 22:04:29

@Bakuriu 2010-11-17 15:18:34

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.

@Seaux 2013-05-30 17:50:01

nice, this solution does seem a bit more direct in expressing the purpose rather than dealing with signals.

@Stéphane 2019-03-20 11:37:04

Using multiprocessing library, I'm not sure on which object I should add those methods .. any clue ?

@Bakuriu 2019-03-20 21:21:16

@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.

@user395760 2010-11-17 14:27:39

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

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

@Alex 2010-11-17 14:29:43

I want clean exit without trace if user press ctrl-c

@Steven Rumbalski 2010-11-17 14:51:26

catched ==> caught

@Matt 2012-12-20 14:17:50

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.

@user395760 2012-12-20 17:31:38

Yeah, I learned that about three minutes after posting, when kotlinski's answer rolled in ;)

@hangtwenty 2013-11-05 14:15:54

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:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

You can also use it as a decorator (as of 2.6; this example is from the docs):

import atexit

@atexit.register
def goodbye():
    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:

@TimZaman 2018-11-23 00:59:27

atexit doesnt' work for KeyboardInterrupt (python 3.7)

@bgporter 2010-11-17 14:32:56

If all you want is to not show the traceback, make your code like this:

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(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)

@Hal Canary 2013-07-13 18:15:18

For some reason, this doesn't always work for me. signal.signal( signal.SIGINT, lambda s, f : sys.exit(0)) always does.

@Sudo Bash 2013-10-23 01:56:45

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.

@bgporter 2013-10-23 13:46:56

There's another SO question specifically about Ctrl+C with pygtk: stackoverflow.com/questions/16410852/…

Related Questions

Sponsored Content

15 Answered Questions

[SOLVED] What are metaclasses in Python?

23 Answered Questions

[SOLVED] What is the difference between @staticmethod and @classmethod?

28 Answered Questions

56 Answered Questions

[SOLVED] Calling an external command in Python

7 Answered Questions

[SOLVED] Manually raising (throwing) an exception in Python

10 Answered Questions

25 Answered Questions

[SOLVED] How can I safely create a nested directory in Python?

11 Answered Questions

[SOLVED] How to properly ignore exceptions

17 Answered Questions

[SOLVED] Does Python have a string 'contains' substring method?

7 Answered Questions

[SOLVED] Catch multiple exceptions in one line (except block)

Sponsored Content