Асинхронная обработка исключений, к сожалению, ненадежна (исключения, вызываемые обработчиками сигналов, внешние контексты через C API и т. Д.). Вы можете увеличить свои шансы на правильную обработку асинхронного исключения, если в коде есть некоторая координация в отношении того, какой фрагмент кода отвечает за их перехват (максимальный возможный в стеке вызовов представляется подходящим, за исключением очень важных функций).
Вызываемая функция (dostuff
) или функции, расположенные ниже по стеку, могут сами по себе поймать KeyboardInterrupt или BaseException, которые вы не учли / не смогли учесть.
Этот тривиальный случай прекрасно работал с Python 2.6.6 (x64) Interactive + Windows 7 (64bit):
>>> import time
>>> def foo():
... try:
... time.sleep(100)
... except KeyboardInterrupt:
... print "INTERRUPTED!"
...
>>> foo()
INTERRUPTED! #after pressing ctrl+c
EDIT:
После дальнейшего расследования я попробовал то, что, по моему мнению, является примером, который другие использовали для воспроизведения проблемы. Мне было лень, поэтому я пропустил «наконец»
>>> def foo():
... try:
... sys.stdin.read()
... except KeyboardInterrupt:
... print "BLAH"
...
>>> foo()
Возвращается сразу после нажатия CTRL + C. Интересная вещь произошла, когда я сразу попытался снова вызвать foo:
>>> foo()
Traceback (most recent call last):
File "c:\Python26\lib\encodings\cp437.py", line 14, in decode
def decode(self,input,errors='strict'):
KeyboardInterrupt
Исключение возникло сразу, и я не нажал CTRL + C.
Казалось бы, это имеет смысл - похоже, мы имеем дело с нюансами в том, как обрабатываются асинхронные исключения в Python. Это может занять несколько инструкций байт-кода, прежде чем асинхронное исключение будет фактически выведено, а затем вызвано в текущем контексте выполнения. (Такое поведение я видел, когда играл с ним в прошлом)
См. C API: http://docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc
Таким образом, это несколько объясняет, почему KeyboardInterrupt возникает в контексте выполнения оператора finally в этом примере:
>>> def foo():
... try:
... sys.stdin.read()
... except KeyboardInterrupt:
... print "interrupt"
... finally:
... print "FINALLY"
...
>>> foo()
FINALLY
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in foo
KeyboardInterrupt
Там может быть какое-то безумное смешение пользовательских обработчиков сигналов, смешанных со стандартным обработчиком KeyboardInterrupt / CTRL + C интерпретатора, что приводит к такому поведению. Например, вызов read () видит сигнал и освобождает под залог, но он повторно поднимает сигнал после отмены регистрации его обработчика. Я бы не знал наверняка без проверки кодовой базы интерпретатора.
Вот почему я обычно уклоняюсь от использования асинхронных исключений ....
РЕДАКТИРОВАТЬ 2
Я думаю, что есть хороший повод для сообщения об ошибке.
Опять больше теорий ... (только на основе чтения кода). См. Источник объекта файла: http://svn.python.org/view/python/branches/release26-maint/Objects/fileobject.c?revision=81277&view=markup
file_read вызывает Py_UniversalNewlineFread (). fread может вернуться с ошибкой с errno = EINTR (он выполняет свою собственную обработку сигналов). В этом случае Py_UniversalNewlineFread () выдает ошибку, но не выполняет никакой проверки сигнала с помощью PyErr_CheckSignals (), чтобы обработчики могли вызываться синхронно. file_read очищает ошибку файла, но также не вызывает PyErr_CheckSignals ().
Смотрите примеры использования getline () и getline_via_fgets (). Шаблон описан в этом отчете об ошибке для аналогичной проблемы: (http://bugs.python.org/issue1195). Таким образом, кажется, что сигнал обрабатывается переводчиком в неопределенное время.
Полагаю, что погружение глубже не имеет смысла, поскольку до сих пор не ясно, является ли пример sys.stdin.read () правильным аналогом вашей функции "dostuff ()". (в игре может быть несколько ошибок)