Содержание страницы
signal – Установка обработчиков асинхронных событий¶signal – Set handlers for asynchronous events
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила¶General rules
Функция signal.signal() позволяет определять пользовательские обработчики,
выполняемые при получении сигнала. Небольшое количество обработчиков по умолчанию
установлено: сигнал SIGPIPE игнорируется (поэтому ошибки записи в каналы и сокеты
могут сообщаться как обычные исключения Python), а сигнал SIGINT
преобразуется в исключение KeyboardInterrupt, если родительский процесс
не изменил его.
Обработчик конкретного сигнала после установки остается активным до
явной переустановки (Python эмулирует интерфейс в стиле BSD независимо от
реализации), за исключением обработчика для
SIGCHLD, который следует базовой реализации.
На платформах WebAssembly сигналы эмулируются и поэтому ведут себя иначе. Некоторые функции и сигналы на этих платформах недоступны.
Выполнение обработчиков сигналов Python¶Execution of Python signal handlers
Обработчик сигнала Python не выполняется внутри низкоуровневого (C) обработчика сигналов. Вместо этого низкоуровневый обработчик устанавливает флаг, который сообщает виртуальной машине выполнить соответствующий обработчик сигнала Python в более поздний момент (например, на следующей инструкции байткода). Это имеет последствия:
Ловить синхронные ошибки вроде
SIGFPEилиSIGSEGV, вызванные недопустимой операцией в коде C, не имеет смысла. Python вернется от обработчика сигнала к коду C, который, скорее всего, снова возбудит тот же сигнал, что приведет к зависанию Python. Начиная с Python 3.3 можно использовать модульfaulthandlerдля сообщения о синхронных ошибках.Длительное вычисление, реализованное чисто на C (например, сравнение с регулярным выражением по большому объему текста), может выполняться без прерывания сколь угодно долго, независимо от полученных сигналов. Обработчики сигналов Python будут вызваны по завершении вычисления.
Если обработчик возбуждает исключение, оно будет возбуждено «из ниоткуда» в основном потоке. См. примечание ниже для обсуждения.
Сигналы и потоки¶Signals and threads
Обработчики сигналов Python всегда выполняются в главном потоке Python главного интерпретатора,
даже если сигнал был получен в другом потоке. Это означает, что сигналы
нельзя использовать как средство межпотокового взаимодействия. Вместо этого можно использовать
примитивы синхронизации из модуля threading.
Кроме того, только главный поток главного интерпретатора может устанавливать новый обработчик сигнала.
Предупреждение
Примитивы синхронизации, такие как threading.Lock, не следует использовать
внутри обработчиков сигналов. Это может привести к неожиданным взаимоблокировкам.
Содержимое модуля¶Module contents
Изменено в версии 3.5: константы, связанные с сигналами (SIG*), обработчиками (SIG_DFL, SIG_IGN) и сигнальными масками
(SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK)
перечисленные ниже, были преобразованы в
enums (соответственно Signals, Handlers и Sigmasks).
Функции getsignal(), pthread_sigmask(), sigpending() и
sigwait() возвращают человекочитаемые
enums в виде объектов Signals.
Модуль signal определяет три перечисления:
- class signal.Signals¶
enum.IntEnumнабор констант SIG* и констант CTRL_*.Добавлено в версии 3.5.
- class signal.Handlers¶
enum.IntEnumнабор константSIG_DFLиSIG_IGN.Добавлено в версии 3.5.
- class signal.Sigmasks¶
enum.IntEnumнабор константSIG_BLOCK,SIG_UNBLOCKиSIG_SETMASK.Доступность: Unix.
Смотрите man-страницу sigprocmask(2) и pthread_sigmask(3) для получения дополнительной информации.
Добавлено в версии 3.5.
Переменные, определенные в модуле signal:
- signal.SIG_DFL¶
Это один из двух стандартных вариантов обработки сигналов; он просто выполняет функцию по умолчанию для сигнала. Например, на большинстве систем действие по умолчанию для
SIGQUIT– сбросить дамп ядра и завершиться, тогда как действие по умолчанию дляSIGCHLD– просто игнорировать его.
- signal.SIG_IGN¶
Это еще один стандартный обработчик сигнала, который просто игнорирует данный сигнал.
- signal.SIGALRM¶
Сигнал таймера от alarm(2).
Доступность: Unix.
- signal.SIGBREAK¶
Прерывание с клавиатуры (CTRL + BREAK).
Доступность: Windows.
- signal.SIGBUS¶
Ошибка шины (неверный доступ к памяти).
Доступность: Unix.
- signal.SIGCHLD¶
Дочерний процесс остановлен или завершён.
Доступность: Unix.
- signal.SIGCLD¶
Псевдоним для
SIGCHLD.Доступность: не macOS.
- signal.SIGCONT¶
Продолжить процесс, если он в данный момент остановлен
Доступность: Unix.
- signal.SIGFPE¶
Исключение с плавающей запятой. Например, деление на ноль.
См. также
ZeroDivisionErrorвозникает, когда второй аргумент операции деления или взятия остатка равен нулю.
- signal.SIGHUP¶
Обнаружен обрыв связи на управляющем терминале или завершение управляющего процесса.
Доступность: Unix.
- signal.SIGILL¶
Недопустимая инструкция.
- signal.SIGINT¶
Прерывание с клавиатуры (CTRL + C).
Действие по умолчанию – возбудить
KeyboardInterrupt.
- signal.SIGKILL¶
Сигнал уничтожения.
Его нельзя перехватить, заблокировать или проигнорировать.
Доступность: Unix.
- signal.SIGPIPE¶
Разрыв канала: запись в канал без читателей.
Действие по умолчанию – игнорировать сигнал.
Доступность: Unix.
- signal.SIGPROF¶
Таймер профилирования истёк.
Доступность: Unix.
- signal.SIGQUIT¶
Сигнал выхода терминала.
Доступность: Unix.
- signal.SIGSEGV¶
Ошибка сегментации: неверная ссылка на память.
- signal.SIGSTOP¶
Остановка выполнения (не может быть перехвачен или проигнорирован).
Доступность: Unix.
- signal.SIGSTKFLT¶
Сбой стека на сопроцессоре. Ядро Linux не генерирует этот сигнал: он может быть вызван только из пользовательского пространства.
Доступность: Linux.
На архитектурах, где этот сигнал доступен. См. страницу руководства signal(7) для получения дополнительных сведений.
Добавлено в версии 3.12.
- signal.SIGTERM¶
Сигнал завершения.
- signal.SIGUSR1¶
Пользовательский сигнал 1.
Доступность: Unix.
- signal.SIGUSR2¶
Пользовательский сигнал 2.
Доступность: Unix.
- signal.SIGVTALRM¶
Виртуальный таймер истек.
Доступность: Unix.
- signal.SIGWINCH¶
Сигнал изменения размера окна.
Доступность: Unix.
- signal.SIGXCPU¶
Превышен лимит процессорного времени.
Доступность: Unix.
- SIG*
Все номера сигналов определены символически. Например, сигнал сброса определен как
signal.SIGHUP; имена переменных идентичны именам, используемым в программах на C, как в<signal.h>. В man-странице Unix для ‘signal’ перечислены существующие сигналы (в некоторых системах это signal(2), в других список находится в signal(7)). Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; только те имена, которые определены системой, определены в этом модуле.
- signal.CTRL_C_EVENT¶
Сигнал, соответствующий комбинации клавиш Ctrl+C. Этот сигнал может использоваться только с
os.kill().Доступность: Windows.
Добавлено в версии 3.2.
- signal.CTRL_BREAK_EVENT¶
Сигнал, соответствующий комбинации клавиш Ctrl+Break. Этот сигнал может использоваться только с
os.kill().Доступность: Windows.
Добавлено в версии 3.2.
- signal.NSIG¶
На единицу больше самого высокого номера сигнала. Используйте
valid_signals()для получения допустимых номеров сигналов.
- signal.ITIMER_REAL¶
Уменьшает интервальный таймер в реальном времени и отправляет
SIGALRMпо истечении.
- signal.ITIMER_VIRTUAL¶
Уменьшает интервальный таймер только во время выполнения процесса и отправляет SIGVTALRM по истечении.
- signal.ITIMER_PROF¶
Уменьшает интервальный таймер как при выполнении процесса, так и при выполнении системой от имени процесса. В паре с ITIMER_VIRTUAL этот таймер обычно используется для профилирования времени, затраченного приложением в пользовательском и ядерном пространстве. SIGPROF доставляется по истечении времени.
- signal.SIG_BLOCK¶
Возможное значение параметра how функции
pthread_sigmask(), указывающее, что сигналы должны быть заблокированы.Добавлено в версии 3.3.
- signal.SIG_UNBLOCK¶
Возможное значение параметра how функции
pthread_sigmask(), указывающее, что сигналы должны быть разблокированы.Добавлено в версии 3.3.
- signal.SIG_SETMASK¶
Возможное значение параметра how функции
pthread_sigmask(), указывающее, что маска сигналов должна быть заменена.Добавлено в версии 3.3.
Модуль signal определяет одно исключение:
- exception signal.ItimerError¶
Возбуждается для указания на ошибку в реализации
setitimer()илиgetitimer(). Этого исключения следует ожидать, если некорректный интервальный таймер или отрицательное время передаются вsetitimer(). Это исключение является подтипомOSError.
Модуль signal определяет следующие функции:
- signal.alarm(time)¶
Если time не равен нулю, эта функция запрашивает отправку сигнала
SIGALRMпроцессу через time секунд. Любой ранее запланированный сигнал отменяется (в любой момент может быть запланирован только один сигнал). Возвращаемое значение – количество секунд до отправки предыдущего запланированного сигнала. Если time равно нулю, никакой сигнал не планируется, а любой запланированный отменяется. Если возвращаемое значение равно нулю, сигнал в настоящее время не запланирован.Доступность: Unix.
Для получения дополнительной информации см. справочную страницу alarm(2).
- signal.getsignal(signalnum)¶
Возвращает текущий обработчик сигнала signalnum. Возвращаемое значение может быть вызываемым объектом Python или одним из специальных значений
signal.SIG_IGN,signal.SIG_DFLилиNone. Здесьsignal.SIG_IGNозначает, что сигнал ранее игнорировался,signal.SIG_DFLозначает, что ранее использовался способ обработки сигнала по умолчанию, аNoneозначает, что предыдущий обработчик сигнала не был установлен из Python.
- signal.strsignal(signalnum)¶
Возвращает описание сигнала signalnum, например «Interrupt» для
SIGINT. ВозвращаетNone, если signalnum не имеет описания. ВозбуждаетValueError, если signalnum недопустим.Добавлено в версии 3.8.
- signal.valid_signals()¶
Возвращает набор допустимых номеров сигналов на этой платформе. Он может быть меньше
range(1, NSIG), если некоторые сигналы зарезервированы системой для внутреннего использования.Добавлено в версии 3.8.
- signal.pause()¶
Приостанавливает выполнение процесса до получения сигнала; затем будет вызван соответствующий обработчик. Ничего не возвращает.
Доступность: Unix.
Для получения дополнительной информации см. справочную страницу signal(2).
См. также
sigwait(),sigwaitinfo(),sigtimedwait()иsigpending().
- signal.raise_signal(signum)¶
Отправляет сигнал вызывающему процессу. Ничего не возвращает.
Добавлено в версии 3.8.
- signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)¶
Отправляет сигнал sig процессу, на который указывает файловый дескриптор pidfd. В настоящее время Python не поддерживает параметр siginfo; он должен быть равен
None. Аргумент flags предусмотрен для будущих расширений; в настоящее время не определено никаких значений флагов.За дополнительной информацией обращайтесь к справочной странице pidfd_send_signal(2).
Доступность: Linux >= 5.1, Android >=
build-timeAPI level 31Добавлено в версии 3.9.
- signal.pthread_kill(thread_id, signalnum)¶
Отправляет сигнал signalnum потоку thread_id, другому потоку в том же процессе, что и вызывающий. Целевой поток может выполнять любой код (Python или нет). Однако если целевой поток выполняет интерпретатор Python, обработчики сигналов Python будут выполнены главным потоком главного интерпретатора. Следовательно, единственный смысл отправки сигнала конкретному потоку Python – принудительно завершить выполняющийся системный вызов с ошибкой
InterruptedError.Используйте
threading.get_ident()или атрибутidentобъектовthreading.Thread, чтобы получить подходящее значение для thread_id.Если signalnum равен 0, сигнал не отправляется, но проверка ошибок по-прежнему выполняется; это можно использовать для проверки, работает ли целевой поток.
Возбуждает событие аудита
signal.pthread_killс аргументамиthread_id,signalnum.Доступность: Unix.
Смотрите man-страницу pthread_kill(3) для получения дополнительной информации.
См. также
os.kill().Добавлено в версии 3.3.
- signal.pthread_sigmask(how, mask)¶
Получает и/или изменяет маску сигналов вызывающего потока. Маска сигналов – это набор сигналов, доставка которых в настоящий момент заблокирована для вызывающей стороны. Возвращает старую маску сигналов в виде набора сигналов.
Поведение вызова зависит от значения how следующим образом.
SIG_BLOCK: Набор заблокированных сигналов – это объединение текущего набора и аргумента mask.SIG_UNBLOCK: Сигналы из mask удаляются из текущего набора заблокированных сигналов. Допускается попытка разблокировать сигнал, который не был заблокирован.SIG_SETMASK: Набор заблокированных сигналов устанавливается равным аргументу mask.
mask – это набор номеров сигналов (например, {
signal.SIGINT,signal.SIGTERM}). Используйтеvalid_signals()для полной маски, включающей все сигналы.Например,
signal.pthread_sigmask(signal.SIG_BLOCK, [])считывает маску сигналов вызывающего потока.SIGKILLиSIGSTOPне могут быть заблокированы.Доступность: Unix.
Смотрите man-страницу sigprocmask(2) и pthread_sigmask(3) для получения дополнительной информации.
См. также
pause(),sigpending()иsigwait().Добавлено в версии 3.3.
- signal.setitimer(which, seconds, interval=0)¶
Устанавливает указанный интервальный таймер (один из
signal.ITIMER_REAL,signal.ITIMER_VIRTUALилиsignal.ITIMER_PROF), определяемый which, так чтобы он срабатывал через seconds (с округлением до микросекунд, в отличие отalarm()), а затем каждые interval секунд (если interval не равен нулю). Интервальный таймер, заданный параметром which, можно сбросить, установив seconds в ноль.Когда срабатывает интервальный таймер, процессу отправляется сигнал. Отправляемый сигнал зависит от используемого таймера:
signal.ITIMER_REALдоставляетSIGALRM,signal.ITIMER_VIRTUALотправляетSIGVTALRM, аsignal.ITIMER_PROFдоставляетSIGPROF.Старые значения возвращаются в виде кортежа из двух чисел с плавающей запятой: (
delay,interval).Попытка передать недопустимый интервальный таймер приведет к
ItimerError.Доступность: Unix.
Изменено в версии 3.15: Принимает любые вещественные числа в качестве seconds и interval, а не только целые числа или числа с плавающей запятой.
- signal.getitimer(which)¶
Возвращает текущее значение заданного интервального таймера, указанного параметром which.
Доступность: Unix.
- signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)¶
Устанавливает файловый дескриптор пробуждения в fd. Когда получен сигнал, для которого программа зарегистрировала обработчик, номер сигнала записывается одним байтом в этот fd. Если для интересующих сигналов не зарегистрирован обработчик, то в fd пробуждения ничего не записывается. Это может использоваться библиотекой для пробуждения вызова poll или select, что позволяет полностью обработать сигнал.
Возвращается старый fd пробуждения (или -1, если пробуждение через файловый дескриптор не было включено). Если fd равен -1, пробуждение через файловый дескриптор отключается. Если не -1, то fd должен быть неблокирующим. Задача библиотеки – удалить все байты из fd перед повторным вызовом poll или select.
Если потоки включены, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из других потоков приведёт к возбуждению исключения
ValueError.Существует два распространенных способа использования этой функции. В обоих подходах используется fd для пробуждения при поступлении сигнала, но затем они различаются тем, как определяют, which сигнал или сигналы поступили.
В первом подходе данные читаются из буфера fd, и значения байтов дают номера сигналов. Это просто, но в редких случаях может возникнуть проблема: обычно fd имеет ограниченный объём буфера, и если слишком много сигналов приходит слишком быстро, буфер может переполниться, и некоторые сигналы могут быть потеряны. Если используется этот подход, следует установить
warn_on_full_buffer=True, что, по крайней мере, приведет к выводу предупреждения в stderr при потере сигналов.Во втором подходе fd пробуждения используется only для пробуждения, а фактические значения байтов игнорируются. В этом случае важно лишь то, пуст буфер fd или нет; полный буфер не свидетельствует о проблеме. Если используется этот подход, следует установить
warn_on_full_buffer=False, чтобы пользователи не сбивались с толку ложными предупреждениями.Изменено в версии 3.5: В Windows функция теперь также поддерживает дескрипторы сокетов.
Изменено в версии 3.7: Добавлен параметр
warn_on_full_buffer.
- signal.siginterrupt(signalnum, flag)¶
Изменяет поведение перезапуска системных вызовов: если flag равен
False, системные вызовы будут перезапускаться при прерывании сигналом signalnum, в противном случае системные вызовы будут прерваны. Ничего не возвращает.Доступность: Unix.
За дополнительной информацией обращайтесь к man-странице siginterrupt(3).
Обратите внимание, что установка обработчика сигнала с помощью
signal()сбрасывает поведение перезапуска на прерываемое, неявно вызываяsiginterrupt()со значением flag, равным true, для данного сигнала.
- signal.signal(signalnum, handler)¶
Устанавливает обработчик для сигнала signalnum в функцию handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже), или одним из специальных значений
signal.SIG_IGNилиsignal.SIG_DFL. Возвращается предыдущий обработчик сигнала (см. описаниеgetsignal()выше). (За дополнительной информацией обращайтесь к man-странице Unix signal(2).)Если потоки включены, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из других потоков приведёт к возбуждению исключения
ValueError.handler вызывается с двумя аргументами: номером сигнала и текущим стековым кадром (
Noneили объектом кадра; описание объектов кадра см. в описании в иерархии типов или в описаниях атрибутов модуляinspect).В Windows
signal()можно вызывать только сSIGABRT,SIGFPE,SIGILL,SIGINT,SIGSEGV,SIGTERMилиSIGBREAK. В любом другом случае будет возбужденоValueError. Обратите внимание, что не все системы определяют одинаковый набор имён сигналов;AttributeErrorбудет возбуждено, если имя сигнала не определено как константа уровня модуляSIG*.
- signal.sigpending()¶
Проверяет набор сигналов, ожидающих доставки вызывающему потоку (т.е. сигналы, которые были подняты, пока были заблокированы). Возвращает набор ожидающих сигналов.
Доступность: Unix.
За дополнительной информацией обращайтесь к man-странице sigpending(2).
См. также
pause(),pthread_sigmask()иsigwait().Добавлено в версии 3.3.
- signal.sigwait(sigset)¶
Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.
Доступность: Unix.
За дополнительной информацией обращайтесь к man-странице sigwait(3).
См. также
pause(),pthread_sigmask(),sigpending(),sigwaitinfo()иsigtimedwait().Добавлено в версии 3.3.
- signal.sigwaitinfo(sigset)¶
Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал и удаляет его из списка ожидающих сигналов. Если один из сигналов в sigset уже ожидает вызывающий поток, функция немедленно вернётся с информацией об этом сигнале. Обработчик сигнала не вызывается для доставленного сигнала. Функция возбуждает
InterruptedError, если она была прервана сигналом, которого нет в sigset.Возвращаемое значение – это объект, представляющий данные, содержащиеся в структуре
siginfo_t, а именно:si_signo,si_code,si_errno,si_pid,si_uid,si_status,si_band.Доступность: Unix.
За дополнительной информацией обращайтесь к man-странице sigwaitinfo(2).
См. также
pause(),sigwait()иsigtimedwait().Добавлено в версии 3.3.
Изменено в версии 3.5: Теперь функция повторяется, если прервана сигналом, не входящим в sigset, и обработчик сигнала не возбуждает исключение (см. PEP 475 для обоснования).
- signal.sigtimedwait(sigset, timeout)¶
Как
sigwaitinfo(), но принимает дополнительный аргумент timeout, указывающий тайм-аут. Если timeout указан как0, выполняется опрос. ВозвращаетNoneпри наступлении тайм-аута.Доступность: Unix.
За дополнительной информацией обращайтесь к man-странице sigtimedwait(2).
См. также
pause(),sigwait()иsigwaitinfo().Добавлено в версии 3.3.
Изменено в версии 3.5: Теперь функция повторяется с пересчитанным timeout, если прервана сигналом, не входящим в sigset, и обработчик сигнала не возбуждает исключение (см. PEP 475 для обоснования).
Изменено в версии 3.15: Принимает любое вещественное число в качестве timeout, а не только целое число или число с плавающей точкой.
Примеры¶Examples
Вот минимальный пример программы. Она использует функцию alarm() для ограничения
времени, затрачиваемого на ожидание открытия файла; это полезно, если файл находится на
последовательном устройстве, которое может быть выключено, что обычно приводит к
бесконечному зависанию os.open(). Решение – установить 5-секундный будильник
перед открытием файла; если операция занимает слишком много времени, будет отправлен
сигнал будильника, и обработчик возбудит исключение.
import signal, os
def handler(signum, frame):
signame = signal.Signals(signum).name
print(f'Signal handler called with signal {signame} ({signum})')
raise OSError("Couldn't open device!")
# Установить обработчик сигнала и 5-секундный будильник
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# Этот вызов open() может зависнуть навсегда
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Отключить будильник
Примечание о SIGPIPE¶Note on SIGPIPE
Перенаправление вывода программы через конвейер в такие утилиты, как head(1), приводит
к отправке сигнала SIGPIPE процессу, когда получатель
его стандартного вывода закрывается рано. Это вызывает исключение,
подобное BrokenPipeError: [Errno 32] Broken pipe. Для обработки этого случая точку входа следует
обернуть для перехвата этого исключения следующим образом:
import os
import sys
def main():
try:
# имитировать большой объем вывода (ваш код заменяет этот цикл)
for x in range(10000):
print("y")
# сбросить вывод здесь, чтобы принудительно вызвать SIGPIPE
# пока внутри этого блока try.
sys.stdout.flush()
except BrokenPipeError:
# Python сбрасывает стандартные потоки при выходе; перенаправьте оставшийся вывод в /dev/null, чтобы избежать ещё одного BrokenPipeError при завершении работы.
# в devnull, чтобы избежать еще одного BrokenPipeError при завершении
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python завершается с кодом ошибки 1 при EPIPE
if __name__ == '__main__':
main()
Не следует устанавливать для SIGPIPE действие SIG_DFL,
чтобы избежать BrokenPipeError. Это приведёт к
неожиданному завершению программы всякий раз, когда любое сокетное
соединение будет прервано, пока программа ещё пишет в
него.
Примечание об обработчиках сигналов и исключениях¶Note on Signal Handlers and Exceptions
Если обработчик сигнала возбуждает исключение, оно будет распространено на
главный поток и может быть возбуждено после любой инструкции байт-кода. В первую
очередь, KeyboardInterrupt может появиться в любой точке выполнения.
Большая часть кода Python, включая стандартную библиотеку, не может быть сделана устойчивой против
этого, поэтому KeyboardInterrupt (или любое другое исключение, возникшее в результате
работы обработчика сигнала) может в редких случаях перевести программу в непредвиденное состояние.
Чтобы проиллюстрировать эту проблему, рассмотрим следующий код:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# Если KeyboardInterrupt возникает здесь, все в порядке
self.lock.acquire()
# Если KeyboardInterrupt возникает здесь, __exit__ не будет вызван
...
# KeyboardInterrupt может возникнуть прямо перед возвратом функции
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
Для многих программ, особенно тех, которые просто хотят завершиться по KeyboardInterrupt, это не является проблемой, но приложения, которые являются сложными или требуют высокой надёжности, должны избегать возбуждения исключений в обработчиках сигналов. Им также следует избегать перехвата KeyboardInterrupt как способа корректного завершения. Вместо этого они должны установить собственный обработчик SIGINT. Ниже приведён пример HTTP-сервера, который избегает KeyboardInterrupt:
import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler
interrupt_read, interrupt_write = socket.socketpair()
def handler(signum, frame):
print('Signal handler called with signal', signum)
interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)
def serve_forever(httpd):
sel = DefaultSelector()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
while True:
for key, _ in sel.select():
if key.fileobj == interrupt_read:
interrupt_read.recv(1)
return
if key.fileobj == httpd:
httpd.handle_request()
print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")