8
import signal
import sys
import time

def sigint_handler(signal, frame):
    print "signal"
    sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)

while 1:
    try:
        print "text"
        time.sleep(2)
    except KeyboardInterrupt:
        print "keybi"
        exit(0)
    except:
        print "except"
        continue

When I press Ctrl-C I see "signal" and "except" and the program doesn't exit.

  1. Why doesn't the program exit, while it apparently reaches sys.exit(0)?

  2. Why doesn't the program flow reach the KeyboardInterrupt section?

  3. What is the concise way to make Ctrl-C work and handle each except: case separately in different places without exiting?

jotik
  • 17,044
  • 13
  • 58
  • 123
noname7619
  • 3,370
  • 3
  • 21
  • 26
  • 2
    Possible duplicate of [How do I capture SIGINT in Python?](http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python) – DeepSpace Apr 09 '16 at 08:13
  • Duplicate doesn't answer 1, which is answered below – Foon Apr 09 '16 at 12:14

2 Answers2

10

The program doesn't exit because sys.exit works by throwing a SystemExit exception, and your blanket except caught it.

The except KeyboardInterrupt doesn't fire because the SIGINT handler you installed overrides the default SIGINT handler, and the default SIGINT handler is responsible for raising a KeyboardInterrupt when you hit Ctrl-C.

As for your third question, it's unclear what you're asking.

user2357112
  • 260,549
  • 28
  • 431
  • 505
3

@user2357112 answered your first two questions, as to your third, you can create your own exception in the signal handler. In this example we raise MyError in the event of a SIGINT.

import signal
import time

class MyError(Exception): 
    pass

def handler(sig, frame):
    raise MyError('Received signal ' + str(sig) +
                  ' on line ' + str(frame.f_lineno) +
                  ' in ' + frame.f_code.co_filename)    

signal.signal(signal.SIGINT, handler)

try:
    while 1:
        time.sleep(1)        # Hit <CTRL>+C here
except KeyboardInterrupt:
    print('Keyboard interrupt caught')
except MyError as err:
    print("Hiccup:",err)
    # do stuff

print('Clean exit')

If you comment out the call to signal.signal() then you will get a KeyboardInterrupt, as described.

cdarke
  • 42,728
  • 8
  • 80
  • 84
  • And what if I want to do some action and then exit regardless of what "except" blocks are there? – noname7619 Apr 09 '16 at 10:22
  • Ah ok, I'll just re-raise KeyboardInterrupt. – noname7619 Apr 09 '16 at 10:24
  • Just call `sys.exit()` to exit. That didn't work last time because you had a "catch-all" `except:`. If you want to force an exit (which you should think carefully about before doing) then you can call `os._exit()`, but that can have strange side effects because clean-up code will not be executed. – cdarke Apr 09 '16 at 13:24
  • By the way, `exit()` is meant only for interactive use, it shouldn't be used in scripts. `exit` and `quit` are actually objects of `site.Quitter` class. Quote from the python documentation "*They are useful for the interactive interpreter shell and should not be used in programs.*" When called they raise a `SystemExit` exception and close `stdin` - IDLE ignores `SystemExit` but closes when stdin is closed. Quite apart from the fact that the `site` module might not be loaded, the side effect could upset code that traps the `SystemExit` exception yet still expects stdin to be there. – cdarke Apr 09 '16 at 13:31