в python, почему существует разница в исключении, когда обработка сигнала выполняется до и после попытки, кроме

Недавно я начал с python. Я играл с обработкой прерывания клавиатуры, когда столкнулся с этим поведением.

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"

но если я изменю обработку сигнала после попытки исключения

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

Когда я нажимаю ctrl+c , в двух случаях возникает разница в исключении. Так почему же такое поведение?


person karyboy    schedule 26.07.2013    source источник
comment
Сделайте правильный отступ, чтобы мы могли лучше понять код   -  person    schedule 26.07.2013
comment
Я проверил этот код с помощью python lint, и он не показывает проблем с отступами.   -  person karyboy    schedule 26.07.2013
comment
Во втором фрагменте обработчик сигнала устанавливается после блока try-except. Так что я думаю, что это не имеет никакого эффекта.   -  person nymk    schedule 26.07.2013


Ответы (2)


В Python есть собственный встроенный обработчик для SIGINT. Этот обработчик просто вызывает KeyboardInterrupt. В вашем первом коде вы заменили встроенный обработчик новым обработчиком, поэтому вы видите этот вывод:

$python test_exc.py 
^Cinterrupted

Обратите внимание, что io interrupted не печатается, так как исключений не возникало. Фактически изменение кода на:

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"

Вы получаете:

$python test_exc.py 
^Cinterrupted

done

Обратите внимание, что нажатие Ctrl+C не блокирует вызов sys.stdin.read(1), поэтому вам все равно нужно нажать какую-нибудь клавишу, чтобы программа продолжила работу. Вызов исключения внутри обработчика сигнала приведет к его возникновению, как если бы оно было вызвано вызовом sys.stdin.read(1):

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"

Пример запуска:

$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

Примечание: вы можете получить доступ к обработчику сигналов по умолчанию через signal.default_int_handler.

person Bakuriu    schedule 26.07.2013
comment
но в моем случае io interrupted печатается, а оператор else не - person karyboy; 26.07.2013
comment
@karyboy Какую ОС вы используете и какую версию Python? Я провел тесты с Python 2.7.4 на Kubuntu 13.04 (ядро Linux: 3.8.0). - person Bakuriu; 26.07.2013
comment
@Bakuriu Я использую Ubuntu 12.04, Python 2.7.3 - person karyboy; 26.07.2013
comment
В Debian 7, ядро ​​3.2, python 2.7.3 (GCC 4.7.2) я получаю тот же результат, что и Бакуриу. Но в Ubuntu 12.04, python 2.7.3 (GCC 4.6.3) я получаю результат karyboy (прерывание io на первом скрипте). - person Paulo Almeida; 26.07.2013

Когда вы пытаетесь зарегистрировать сигнал после блокирующего вызова sys.stdin.read, на самом деле вы никогда этого не добьетесь.

Таким образом, вы получаете исключение, когда нажимаете Ctrl-C, которое вызывает разрыв KeyboardInterrupt из чтения и перехватывается try.

Когда вы на самом деле регистрируете обработчик сигнала в первом примере, происходит нечто немного другое. Прерывание обрабатывается вашим кодом handleInt.

person aychedee    schedule 26.07.2013