signal — Установка обработчиков асинхронных событий
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила
Функция signal.signal() позволяет определять пользовательские обработчики, которые будут выполняться при получении сигнала. Устанавливается небольшое число обработчиков по умолчанию: SIGPIPE игнорируется (чтобы ошибки записи в каналы и сокеты могли сообщаться как обычные исключения Python), а SIGINT преобразуется в исключение KeyboardInterrupt, если родительский процесс не изменил его обработчик.
После установки обработчик определённого сигнала остаётся установленным, пока его явно не сбросят (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации). Исключение составляет обработчик SIGCHLD, который следует поведению базовой реализации.
На платформах WebAssembly wasm32-emscripten и wasm32-wasi сигналы эмулируются и поэтому ведут себя иначе. На этих платформах некоторые функции и сигналы недоступны.
Выполнение обработчиков сигналов Python
Обработчик сигнала Python не выполняется внутри низкоуровневого обработчика сигнала (на C). Вместо этого низкоуровневый обработчик сигнала устанавливает флаг, который сообщает виртуальной машине, что соответствующий обработчик сигнала Python нужно выполнить позднее (например, при выполнении следующей инструкции байткода). Это имеет следующие последствия:
- Нет особого смысла перехватывать синхронные ошибки, такие как
SIGFPEилиSIGSEGV, вызванные недопустимой операцией в коде на C. Python вернётся из обработчика сигнала в код на C, который, вероятно, снова вызовет тот же сигнал, из-за чего Python, по-видимому, зависнет. Начиная с Python 3.3, для диагностики синхронных ошибок можно использовать модульfaulthandler. - Долговременное вычисление, реализованное исключительно на C (например, сопоставление регулярного выражения с большим объёмом текста), может выполняться без прерывания произвольное время независимо от полученных сигналов. Обработчики сигналов Python будут вызваны после завершения вычисления.
- Если обработчик вызывает исключение, оно возникнет в главном потоке как будто из ниоткуда. Обсуждение см. в примечании ниже.
Сигналы и потоки
Обработчики сигналов Python всегда выполняются в главном потоке Python основного интерпретатора, даже если сигнал был получен в другом потоке. Это означает, что сигналы нельзя использовать для межпоточного взаимодействия. Вместо этого можно использовать примитивы синхронизации из модуля threading.
Кроме того, устанавливать новый обработчик сигнала может только главный поток основного интерпретатора.
Содержимое модуля
Изменено в версии 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.
Дополнительную информацию см. на страницах руководства 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.SIGSEGV -
Ошибка сегментации: недопустимая ссылка на память.
-
signal.SIGSTKFLT -
Ошибка стека сопроцессора. Ядро Linux не генерирует этот сигнал: он может быть сгенерирован только в пользовательском пространстве.
Доступность: Linux.
В архитектурах, где этот сигнал доступен. Дополнительную информацию см. на странице руководства signal(7).
Добавлено в версии 3.11.
-
signal.SIGTERM -
Сигнал завершения.
-
signal.SIGUSR1 -
Пользовательский сигнал 1.
Доступность: Unix.
-
signal.SIGUSR2 -
Пользовательский сигнал 2.
Доступность: Unix.
-
signal.SIGWINCH -
Сигнал изменения размера окна.
Доступность: 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 нет описания. Вызывает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
Добавлено в версии 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.0) -
Устанавливает заданный интервальный таймер (один из
signal.ITIMER_REAL,signal.ITIMER_VIRTUALилиsignal.ITIMER_PROF), указанный параметром which, так, чтобы он сработал через seconds (допускается значение типа float, в отличие от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, пробуждение с помощью файлового дескриптора отключается. Если fd не равен -1, он должен быть неблокирующим. Библиотека должна удалять все байты из 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/signal.html