Ответ 1
Вопрос user2357112 связан, объясняет это каким-то образом: Почему я не могу обработать KeyboardInterrupt в python?.
Прерывание клавиатуры повышается асинхронно, поэтому он не сразу завершает работу приложения. Вместо этого Ctrl+C обрабатывается в каком-то контуре событий, который занимает некоторое время, чтобы добраться туда. Это, к сожалению, означает, что вы не можете надежно поймать KeyboardInterrupt
в этом случае. Но мы можем сделать кое-что, чтобы добраться туда.
Как я объяснил вчера, исключение, которое останавливает вызов raw_input
, не является KeyboardInterrupt
, а EOFError
. Вы можете легко проверить это, изменив функцию bing
следующим образом:
def bing():
try:
raw_input()
except Exception as e:
print(type(e))
Вы увидите, что напечатанный тип исключения EOFError
, а не KeyboardInterrupt
. Вы также увидите, что print
даже не прошел полностью: нет новой строки. Похоже, что выход был прерван прерыванием, которое появилось сразу после того, как оператор print написал тип исключения для stdout. Вы также можете увидеть это, когда вы добавляете немного больше материала в печать:
def bing():
try:
raw_input()
except EOFError as e:
print 'Exception raised:', 'EOF Error'
Обратите внимание, что Im использует два отдельных аргумента для оператора печати. Когда мы это выполним, мы можем увидеть текст "Исключено", но "Ошибка EOF" не появится. Вместо этого будет вызываться except
от внешнего вызова и прерывается прерывание клавиатуры.
Тем не менее, в Python 3 ситуация немного вышла из-под контроля. Возьмите этот код:
def bing():
try:
input()
except Exception as e:
print('Exception raised:', type(e))
try:
bing()
print('After bing')
except KeyboardInterrupt:
print('Final KeyboardInterrupt')
Это в значительной степени то, что мы делали раньше, просто исправлены для синтаксиса Python 3. Если я запустил это, я получаю следующий вывод:
Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt
Итак, мы можем снова увидеть, что EOFError правильно пойман, но по какой-то причине Python 3 продолжает выполнение намного дольше, чем Python 2 здесь, так как выполняется печать после bing()
. Хуже того, в некоторых исполнениях с cmd.exe я получаю результат, что прерывание клавиатуры не было обнаружено вообще (так что, по-видимому, прерывание получило обработку после завершения программы).
Итак, что мы можем сделать с этим, если хотим убедиться, что мы получим прерывание клавиатуры? Мы точно знаем, что прерывание подсказки input()
(или raw_input()
) всегда вызывает EOFError
: То, что мы постоянно видели. Итак, что мы можем сделать, это просто поймать это, а затем убедиться, что мы получим прерывание клавиатуры.
Один из способов сделать это - просто поднять KeyboardInterrupt
из обработчика исключений для EOFError
. Но это не только чувствует себя немного грязным, но и не гарантирует, что прерывание на самом деле является тем, что в первую очередь прекратило вводное приглашение (кто знает, что еще может поднять EOFError?). Таким образом, мы должны иметь уже существующий сигнал прерывания, генерирующий исключение.
То, как мы это делаем, довольно просто: мы ждем. До сих пор наша проблема заключалась в том, что исполнение продолжалось, потому что исключение не достигало достаточно быстро. Итак, что, если мы немного подождем, чтобы исключение получилось, прежде чем мы продолжим с другими вещами?
import time
def bing():
try:
input() # or raw_input() for Python 2
except EOFError:
time.sleep(1)
try:
bing()
print('After bing')
except KeyboardInterrupt:
print('Final KeyboardInterrupt')
Теперь мы просто поймаем EOFError и немного подождем, чтобы асинхронные процессы в задней части уладились и решили, нарушить выполнение или нет. Это позволяет мне поймать KeyboardInterrupt
во внешнем try/catch и не будет печатать ничего, кроме того, что я делаю в обработчике исключений.
Вы можете опасаться, что одна секунда - долго ждать, но в наших случаях, когда мы прерываем выполнение, эта секунда никогда не длится долго. Всего за несколько миллисекунд после time.sleep
прерывание поймано и было в нашем обработчике исключений. Таким образом, одна секунда - просто отказоустойчивость, которая будет ждать достаточно долго, чтобы исключение, безусловно, прибыло вовремя. И в худшем случае, когда на самом деле нет прерывания, а просто "нормального" EOFError? Тогда программа, которая ранее блокировалась бесконечно для ввода пользователем, займет секунду дольше, чтобы продолжить; это не должно быть проблемой всегда (не говоря уже о том, что EOFError, вероятно, очень редко).
Итак, у нас есть наше решение: просто поймайте EOFError и подождите немного. По крайней мере, я надеюсь, что это решение, которое работает на других машинах, чем моя собственная ^ _ ^ "После прошлой ночи я не слишком уверен в этом, но по крайней мере я получил последовательный опыт над всеми терминалами и разными версиями Python.