signal – Установка обработчиков асинхронных событий
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
General rulesОбщие правила
Функция signal.signal() позволяет определять пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: SIGPIPE игнорируется (поэтому ошибки записи в каналы и сокеты могут сообщаться как обычные исключения Python), а SIGINT преобразуется в исключение KeyboardInterrupt, если родительский процесс не изменил его.
Обработчик для конкретного сигнала после установки остается активным до тех пор, пока не будет явно сброшен (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации), за исключением обработчика для SIGCHLD, который следует базовой реализации.
На платформах WebAssembly сигналы эмулируются и поэтому ведут себя иначе. Некоторые функции и сигналы на этих платформах недоступны.
Execution of Python signal handlersВыполнение обработчиков сигналов Python
Обработчик сигнала 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: константы 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¶
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.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.
Дополнительную информацию см. в man-странице 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.
Дополнительную информацию см. в man-странице 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.
См. справочную страницу 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)¶
Устанавливает заданный интервальный таймер (один из
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 используется для пробуждения при поступлении сигнала, но они различаются тем, как определяют, какой сигнал или какие сигналы поступили.
В первом подходе мы читаем данные из буфера fd, и значения байтов дают номера сигналов. Это просто, но в редких случаях может возникнуть проблема: обычно fd имеет ограниченный объём буфера, и если слишком много сигналов поступает слишком быстро, буфер может переполниться, и некоторые сигналы могут быть потеряны. Если вы используете этот подход, вам следует установить
warn_on_full_buffer=True, что, по крайней мере, приведёт к выводу предупреждения в stderr при потере сигналов.Во втором подходе мы используем fd пробуждения только для пробуждения и игнорируем реальные значения байтов. В этом случае нас интересует только то, пуст ли буфер 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, равным истине, для данного сигнала.
- 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.
Дополнительную информацию см. на странице руководства 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) # Отключить будильник
Note on SIGPIPEПримечание о 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 сбрасывает стандартные потоки при выходе; перенаправляет оставшийся вывод
# в 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...")