signal — Установка обработчиков асинхронных событий
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила
Функция signal.signal() позволяет задавать пользовательские обработчики, которые будут выполняться при получении сигнала. Устанавливается небольшое число обработчиков по умолчанию: сигнал SIGPIPE игнорируется (поэтому ошибки записи в каналы и сокеты могут обрабатываться как обычные исключения Python), а сигнал SIGINT преобразуется в исключение KeyboardInterrupt, если родительский процесс не изменил его обработчик.
После установки обработчик для конкретного сигнала остаётся активным, пока его явно не сбросят (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации). Исключение составляет обработчик сигнала SIGCHLD, который следует поведению базовой реализации.
На платформах WebAssembly сигналы эмулируются и поэтому ведут себя иначе. Некоторые функции и сигналы на этих платформах недоступны.
Выполнение обработчиков сигналов Python
Обработчик сигнала Python не выполняется внутри низкоуровневого обработчика сигнала (на C). Вместо этого низкоуровневый обработчик сигнала устанавливает флаг, который указывает виртуальной машине выполнить соответствующий обработчик сигнала Python позднее (например, при выполнении следующей инструкции байт-кода). Это имеет следующие последствия:
- Обрабатывать синхронные ошибки, такие как
SIGFPEилиSIGSEGV, вызванные недопустимой операцией в коде на C, практически бессмысленно. Python вернётся из обработчика сигнала в код на C, который, скорее всего, снова вызовет тот же сигнал, из-за чего Python, по-видимому, зависнет. Начиная с Python 3.3, для сообщения о синхронных ошибках можно использовать модульfaulthandler. - Длительное вычисление, полностью реализованное на C (например, сопоставление с регулярным выражением в большом объёме текста), может выполняться без прерывания произвольное время независимо от полученных сигналов. Обработчики сигналов Python будут вызваны после завершения вычисления.
- Если обработчик вызывает исключение, оно будет вызвано «из ниоткуда» в главном потоке. Обсуждение см. в примечании ниже.
Сигналы и потоки
Обработчики сигналов Python всегда выполняются в главном потоке Python главного интерпретатора, даже если сигнал был получен в другом потоке. Это означает, что сигналы нельзя использовать для межпоточного взаимодействия. Вместо этого можно использовать примитивы синхронизации из модуля threading.
Кроме того, устанавливать новый обработчик сигнала разрешено только главному потоку главного интерпретатора.
Предупреждение
Примитивы синхронизации, такие как threading.Lock, не следует использовать внутри обработчиков сигналов. Это может привести к неожиданным взаимным блокировкам.
Содержимое модуля
Изменено в версии 3.5: перечисленные ниже связанные константы signal (SIG*), handler (SIG_DFL, SIG_IGN) и sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) были преобразованы в enums (Signals, Handlers и Sigmasks соответственно). Функции getsignal(), pthread_sigmask(), sigpending() и sigwait() возвращают человекочитаемые enums в виде объектов Signals.
Модуль signal определяет три перечисления:
-
class signal.Signals -
Коллекция констант SIG* и констант CTRL_* на основе
enum.IntEnum.Добавлено в версии 3.5.
-
class signal.Handlers -
Коллекция констант
SIG_DFLиSIG_IGNна основеenum.IntEnum.Добавлено в версии 3.5.
-
class signal.Sigmasks -
Коллекция констант
SIG_BLOCK,SIG_UNBLOCKиSIG_SETMASKна основеenum.IntEnum.Доступность: Unix.
Дополнительную информацию см. на страницах руководства sigprocmask(2) и pthread_sigmask(3).
Добавлено в версии 3.5.
В модуле signal определены следующие переменные:
-
signal.SIG_DFL -
Это один из двух стандартных вариантов обработки сигнала; он просто выполняет действие по умолчанию для данного сигнала. Например, в большинстве систем действие по умолчанию для
SIGQUIT— создать дамп памяти и завершить процесс, а дляSIGCHLD— просто игнорировать сигнал.
-
signal.SIG_IGN -
Это другой стандартный обработчик сигнала, который просто игнорирует полученный сигнал.
-
signal.SIGABRT -
Сигнал аварийного завершения, отправляемый функцией abort(3).
-
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.11.
-
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>. На странице руководства 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, например «Прерывание» для
SIGINT. ВозвращаетNone, если у signalnum нет описания. Если signalnum недопустим, возбуждает исключениеValueError.Добавлено в версии 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 >= уровень API 31 (
build-time)Добавлено в версии 3.9.
-
signal.pthread_kill(thread_id, signalnum) -
Отправляет сигнал signalnum потоку thread_id — другому потоку в том же процессе, что и вызывающий. Целевой поток может выполнять любой код (Python или другой). Однако если целевой поток выполняет интерпретатор Python, обработчики сигналов Python будут выполняться главным потоком главного интерпретатора. Поэтому единственная цель отправки сигнала конкретному потоку Python — принудительно вызвать ошибку выполняющегося системного вызова
InterruptedError.Чтобы получить подходящее значение для thread_id, используйте
threading.get_ident()или атрибутidentобъектовthreading.Thread.Если signalnum равен 0, сигнал не отправляется, но проверка ошибок выполняется; это можно использовать, чтобы проверить, продолжает ли целевой поток работу.
Возбуждает событие аудита
signal.pthread_killс аргументамиthread_id,signalnum.Доступность: Unix.
Дополнительную информацию см. на странице руководства 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.
Дополнительную информацию см. на страницах руководства sigprocmask(2) и pthread_sigmask(3).
См. также
pause(),sigpending()иsigwait().Добавлено в версии 3.3.
-
-
signal.setitimer(which, seconds, interval=0.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.
-
signal.getitimer(which) -
Возвращает текущее значение заданного интервального таймера, указанного параметром which.
Доступность: Unix.
-
signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True) -
Задаёт файловый дескриптор пробуждения fd. Когда поступает сигнал, для которого программа зарегистрировала обработчик, номер сигнала записывается в fd одним байтом. Если вы не зарегистрировали обработчик сигналов, которые вас интересуют, в файловый дескриптор пробуждения ничего записано не будет. Библиотека может использовать это, чтобы пробудить вызов poll или select и дать возможность полностью обработать сигнал.
Возвращается прежний файловый дескриптор пробуждения (или -1, если пробуждение через файловый дескриптор не было включено). Если fd равен -1, пробуждение через файловый дескриптор отключается. Если значение не равно -1, fd должен быть неблокирующим. Библиотека должна удалять все байты из fd, прежде чем снова вызывать poll или select.
Если поддержка потоков включена, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из других потоков приведёт к возникновению исключения
ValueError.Существует два распространённых способа использовать эту функцию. В обоих случаях файловый дескриптор используется для пробуждения при поступлении сигнала, но способы определения того, какой именно сигнал или сигналы поступили, различаются.
В первом случае мы считываем данные из буфера файлового дескриптора, а значения байтов соответствуют номерам сигналов. Этот способ прост, но в редких случаях может возникнуть проблема: как правило, размер буфера файлового дескриптора ограничен, и если слишком много сигналов поступит за короткое время, буфер может заполниться, а некоторые сигналы могут быть потеряны. При использовании этого способа следует задать
warn_on_full_buffer=True, чтобы при потере сигналов в stderr как минимум выводилось предупреждение.Во втором случае файловый дескриптор пробуждения используется только для пробуждения, а фактические значения байтов игнорируются. В этом случае важно лишь то, пуст ли буфер файлового дескриптора; заполненный буфер не является проблемой. При использовании этого способа следует задать
warn_on_full_buffer=False, чтобы не вводить пользователей в заблуждение ложными предупреждениями.Изменено в версии 3.5: В Windows функция теперь также поддерживает дескрипторы сокетов.
Изменено в версии 3.7: Добавлен параметр
warn_on_full_buffer.
-
signal.siginterrupt(signalnum, flag) -
Изменяет поведение перезапуска системных вызовов: если flag равен
False, системные вызовы будут перезапускаться при прерывании сигналом signalnum; в противном случае системные вызовы будут прерываться. Ничего не возвращает.Доступность: Unix.
Дополнительную информацию см. на странице руководства siginterrupt(3).
Обратите внимание: установка обработчика сигнала с помощью
signal()сбросит поведение перезапуска на прерываемое, неявно вызвавsiginterrupt()со значением flag, равным true, для указанного сигнала.
-
signal.signal(signalnum, handler) -
Задаёт обработчик сигнала signalnum — функцию handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже), или одним из специальных значений
signal.SIG_IGNиsignal.SIG_DFL. Возвращается предыдущий обработчик сигнала (см. приведённое выше описаниеgetsignal()). (Дополнительную информацию см. на странице руководства Unix signal(2).)Если поддержка потоков включена, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из других потоков приведёт к возникновению исключения
ValueError.Обработчик handler вызывается с двумя аргументами: номером сигнала и текущим кадром стека (
Noneили объектом кадра; описание объектов кадров см. в описании в иерархии типов или в описаниях атрибутов модуляinspect).В Windows функцию
signal()можно вызывать только сSIGABRT,SIGFPE,SIGILL,SIGINT,SIGSEGV,SIGTERMилиSIGBREAK. В любом другом случае будет вызвано исключениеValueError. Обратите внимание: не во всех системах определён один и тот же набор имён сигналов; если имя сигнала не определено как константа уровня модуляSIG*, будет вызвано исключениеAttributeError.
-
signal.sigpending() -
Проверяет набор сигналов, ожидающих доставки вызывающему потоку (то есть сигналов, поступивших во время блокировки). Возвращает набор ожидающих сигналов.
Доступность: Unix.
Дополнительную информацию см. на странице руководства sigpending(2).
См. также
pause(),pthread_sigmask()иsigwait().Добавлено в версии 3.3.
-
signal.sigwait(sigset) -
Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.
Доступность: Unix.
Дополнительную информацию см. на странице руководства sigwait(3).
См. также
pause(),pthread_sigmask(),sigpending(),sigwaitinfo()иsigtimedwait().Добавлено в версии 3.3.
-
signal.sigwaitinfo(sigset) -
Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал и удаляет его из списка ожидающих сигналов. Если один из сигналов в sigset уже ожидает обработки вызывающим потоком, функция немедленно возвращает информацию об этом сигнале. Обработчик доставленного сигнала не вызывается. Если функция прерывается сигналом, не входящим в sigset, она вызывает исключение
InterruptedError.Возвращаемое значение — объект, представляющий данные структуры
siginfo_t, а именно:si_signo,si_code,si_errno,si_pid,si_uid,si_status,si_band.Доступность: Unix.
Дополнительную информацию см. на странице руководства sigwaitinfo(2).
См. также
pause(),sigwait()иsigtimedwait().Добавлено в версии 3.3.
Изменено в версии 3.5: Теперь при прерывании сигналом, не входящим в sigset, функция выполняется повторно, если обработчик сигнала не вызывает исключение (обоснование см. в PEP 475).
-
signal.sigtimedwait(sigset, timeout) -
Аналогична
sigwaitinfo(), но принимает дополнительный аргумент timeout, задающий время ожидания. Если для timeout указано значение0, выполняется проверка без ожидания. Если время ожидания истекло, возвращаетсяNone.Доступность: Unix.
Дополнительную информацию см. на странице руководства sigtimedwait(2).
См. также
pause(),sigwait()иsigwaitinfo().Добавлено в версии 3.3.
Изменено в версии 3.5: Теперь при прерывании сигналом, не входящим в sigset, функция выполняется повторно с пересчитанным значением timeout, если обработчик сигнала не вызывает исключение (обоснование см. в PEP 475).
Примеры
Ниже приведён минимальный пример программы. В ней функция 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!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
Примечание о SIGPIPE
Перенаправление вывода программы в такие инструменты, как head(1), приведёт к отправке процессу сигнала SIGPIPE, когда получатель стандартного вывода преждевременно закроет соединение. В результате возникает исключение, например BrokenPipeError: [Errno 32] Broken pipe. Чтобы обработать такую ситуацию, оберните точку входа программы в блок перехвата этого исключения следующим образом:
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
Не задавайте для сигнала SIGPIPE действие по умолчанию SIG_DFL, пытаясь избежать исключения BrokenPipeError. Это приведёт к неожиданному завершению программы при любом прерывании соединения с сокетом, пока программа продолжает в него записывать.
Примечание об обработчиках сигналов и исключениях
Если обработчик сигнала вызывает исключение, оно будет передано главному потоку и может возникнуть после выполнения любой инструкции байткода. В частности, KeyboardInterrupt может возникнуть в любой момент выполнения программы. Большинство кода Python, включая стандартную библиотеку, невозможно сделать устойчивым к этому, поэтому KeyboardInterrupt (или любое другое исключение, вызванное обработчиком сигнала) в редких случаях может привести программу в неожиданное состояние.
Чтобы проиллюстрировать эту проблему, рассмотрим следующий код:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occurs here, __exit__ will not be called
...
# KeyboardInterrupt could occur just before the function returns
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...")
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/signal.html