6

I recently started with python . I was playing with handling the keyboard interrupt , when I came across this behavior

import signal,sys

def handleInt(sign,no):
    print "interrupted"

signal.signal(signal.SIGINT,handleInt)    # exception raised is IOError

try:
    sys.stdin.read(1)
except IOError:
    print "io interrupt"

but if I change the signal handling to after the try-except

import signal,sys

def handleInt(sign,no):
    print "interrupted"

try:
    sys.stdin.read(1)
except KeyboardInterrupt:
    print "keyboard interrupt"

signal.signal(signal.SIGINT,handleInt)    # exception raised is KeyboardInterrupt

When I press ctrl+c , there is a difference in the exception in the two cases .So why is this behavior ?

karyboy
  • 317
  • 1
  • 5
  • 22
  • I have checked this code with a python lint , and it shows no indenting problems – karyboy Jul 26 '13 at 09:34
  • 1
    In the second snippet, the signal handler is installed after the try-except block. Thus I guess it has no effect. – nymk Jul 26 '13 at 09:52

2 Answers2

5

Python has its own built-in singal handler for SIGINT. This handler simply raises KeyboardInterrupt. In your first code, you replaced the built-in handler with the new handler hence you see this output:

$python test_exc.py 
^Cinterrupted

Note that io interrupted is not printed, since no exception was raised. In fact modifying the code to:

import signal,sys

def handleInt(sign,no):
    print "interrupted"

signal.signal(signal.SIGINT, handleInt)    # exception raised is IOError

try:
    sys.stdin.read(1)
except IOError:
    print "io interrupt"
else:
    # else is executed only if no exception was raised
    print "done"

You get:

$python test_exc.py 
^Cinterrupted

done

Note that hitting Ctrl+C does not block the call to sys.stdin.read(1) hence you still have to press some key to let the program continue. Raising an exception inside the signal handler will raise it as if the call to sys.stdin.read(1) produced it:

import signal,sys

def handleInt(sign,no):
    print "interrupted"
    raise OSError

signal.signal(signal.SIGINT, handleInt)    # exception raised is IOError

try:
    sys.stdin.read(1)
except IOError:
    print "io interrupt"
else:
    # else is executed only if no exception was raised
    print "done"

Sample run:

$python test_exc.py 
^Cinterrupted
Traceback (most recent call last):
  File "test_exc.py", line 10, in <module>
    sys.stdin.read(1)
  File "test_exc.py", line 5, in handleInt
    raise OSError
OSError

Note: you can access the default signal handler via signal.default_int_handler.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • but in my case , "io interrupted " is printed and the statement in else is not – karyboy Jul 26 '13 at 11:42
  • @karyboy What OS are you using and which python version? I've done my tests with python 2.7.4 on Kubuntu 13.04 (linux kernel: 3.8.0). – Bakuriu Jul 26 '13 at 11:56
  • @Bakuriu I m using ubuntu 12.04 , python 2.7.3 – karyboy Jul 26 '13 at 12:58
  • On Debian 7, kernel 3.2, python 2.7.3 (GCC 4.7.2) I get the same result as Bakuriu. But on Ubuntu 12.04, python 2.7.3 (GCC 4.6.3), I get karyboy's result ("io interrupt" on first script). – Paulo Almeida Jul 26 '13 at 14:12
1

When you try and register the signal after the blocking call to sys.stdin.read, you never actually get there.

So you get an exception when you hit Ctrl-C which raises a KeyboardInterrupt breaks out of the read and is caught by the try.

When you actually register the signal handler in the first example something slightly different is happening. The interrupt is being handled by your handleInt code.

aychedee
  • 24,871
  • 8
  • 79
  • 83