Ответ 1
Асинхронная обработка исключений, к сожалению, не является надежной (исключения, вызванные обработчиками сигналов, внешними контекстами через API C и т.д.). Вы можете увеличить свои шансы на правильное обращение к асинхронному исключению, если в коде есть некоторая координация относительно того, какая часть кода отвечает за их улов (максимально возможный в стеке вызовов кажется подходящим, за исключением очень важных функций).
Вызываемая функция (dostuff
) или функции, расположенные далее в стеке, могут сами по себе иметь catch для KeyboardInterrupt или BaseException, которые вы не могли/не могли учитывать.
Этот тривиальный случай отлично справился с python 2.6.6 (x64) interactive + Windows 7 (64 бит):
>>> 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. Он может принимать несколько инструкций байткода до того, как исключение async фактически будет вытолкнуто, а затем поднято в текущем контексте выполнения. (Это поведение, которое я видел, играя с ним в прошлом)
См. 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()". (в игре может быть несколько ошибок)