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.
Кроме того, установить новый обработчик сигнала разрешено только главному потоку основного интерпретатора.
Содержимое модуля
Изменено в версии 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.
См. страницу руководства sigprocmask(2) и pthread_sigmask(3) для получения дополнительной информации.
Добавлен в версии 3.5.
Переменные, определенные в модуле signal:
-
signal.SIG_DFL -
Это один из двух стандартных вариантов обработки сигналов; он просто выполнит стандартную функцию для сигнала. Например, на большинстве систем стандартным действием для
SIGQUITявляется вывод core и выход, а стандартным действием для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, 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.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.Возвращаются старые значения в виде кортежа: (задержка, интервал).
Попытка передачи недопустимого таймера интервалов вызовет
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.Существует два распространенных способа использования этой функции. В обоих подходах вы используете 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.
См. страницу руководства 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. Обратите внимание, что не все системы определяют один и тот же набор имён сигналов; исключениеAttributeErrorбудет вызвано, если имя сигнала не определено какSIG*константа уровня модуля.
-
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 уже ожидается вызывающим потоком, функция вернёт информацию об этом сигнале сразу. Обработчик сигнала не вызывается для доставленного сигнала. Функция поднимает
InterruptedError, если прерывается сигналом, который не находится в sigset.Значение возврата — объект, представляющий данные, содержащиеся в структуре
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: Функция теперь повторяет попытку с пересчитанным timeout, если прерывается сигналом, не находящимся в sigset, и обработчик сигнала не вызывает исключение (см. 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.13/library/signal.html