signal — Установка обработчиков для асинхронных событий

Исходный код: Lib/signal.py


Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.

Общие правила

Функция signal.signal() позволяет определить пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: SIGPIPE игнорируется (поэтому об ошибках записи в каналах и сокетах можно сообщать как об обычных исключениях Python), а SIGINT преобразуется в KeyboardInterrupt исключение, если родительский процесс не изменил его.

Обработчик для определенного сигнала, однажды установленный, остается установленным до тех пор, пока он не будет явно сброшен (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации), за исключением обработчика для SIGCHLD, который следует за базовой реализацией.

На платформах WebAssembly wasm32-emscripten и wasm32-wasi сигналы эмулируются и, следовательно, ведут себя по-разному. Некоторые функции и сигналы недоступны на этих платформах.

Выполнение обработчиков сигналов Python

Обработчик сигналов Python не выполняется внутри обработчика сигналов низкого уровня (C). Вместо этого низкоуровневый обработчик сигналов устанавливает флаг, который указывает virtual machine выполнить соответствующий обработчик сигналов Python позже (например, при выполнении следующей инструкции bytecode). Это имеет последствия:

  • Нет особого смысла перехватывать синхронные ошибки, такие как SIGFPE или SIGSEGV, которые вызваны недопустимой операцией в коде на C. Python вернется из обработчика сигнала в код на C, который, скорее всего, снова выдаст тот же сигнал, что, по-видимому, приведет к зависанию Python. Начиная с версии Python 3.3, вы можете использовать модуль faulthandler для сообщения о синхронных ошибках.

  • Длительное вычисление, реализованное исключительно на C (например, сопоставление регулярных выражений с большим объемом текста), может выполняться непрерывно в течение произвольного периода времени, независимо от полученных сигналов. Обработчики сигналов Python будут вызваны после завершения вычисления.

  • Если обработчик создает исключение, оно будет создано «из воздуха» в главном потоке. Обсуждение смотрите в note below.

Сигналы и потоки

Обработчики сигналов 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 объектов.

Сигнальный модуль определяет три перечисления:

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.

Availability: 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).

Availability: Unix.

signal.SIGBREAK

Прерывание с клавиатуры (CTRL + BREAK).

Availability: Окна.

signal.SIGBUS

Ошибка шины (плохой доступ к памяти).

Availability: Unix.

signal.SIGCHLD

Дочерний процесс остановлен или завершается некорректно.

Availability: Unix.

signal.SIGCLD

Псевдоним для SIGCHLD.

Availability: не для mac OS.

signal.SIGCONT

Продолжайте процесс, если он в данный момент остановлен

Availability: Unix.

signal.SIGFPE

Исключение с плавающей запятой. Например, деление на ноль.

См.также

ZeroDivisionError вызывается, когда второй аргумент операции деления или умножения по модулю равен нулю.

signal.SIGHUP

Обнаружено зависание на управляющем терминале или прекращение процесса управления.

Availability: Unix.

signal.SIGILL

Незаконное обучение.

signal.SIGINT

Прерывание с клавиатуры (CTRL + C).

Действие по умолчанию - поднять значение KeyboardInterrupt.

signal.SIGKILL

Сигнал к уничтожению.

Его нельзя перехватить, заблокировать или проигнорировать.

Availability: Unix.

signal.SIGPIPE

Поврежденный канал: запись в канал без считывателей.

Действие по умолчанию заключается в игнорировании сигнала.

Availability: Unix.

signal.SIGSEGV

Ошибка сегментации: неверное обращение к памяти.

signal.SIGSTKFLT

Ошибка стека в сопроцессоре. Ядро Linux не выдает этот сигнал: он может быть получен только в пользовательском пространстве.

Availability: Linux.

На архитектурах, где доступен сигнал. Дополнительную информацию смотрите на странице руководства signal(7).

Добавлено в версии 3.11.

signal.SIGTERM

Сигнал завершения.

signal.SIGUSR1

Определяемый пользователем сигнал 1.

Availability: Unix.

signal.SIGUSR2

Определяемый пользователем сигнал 2.

Availability: Unix.

signal.SIGWINCH

Сигнал об изменении размера окна.

Availability: Unix.

SIG*

Все номера сигналов обозначены символически. Например, сигнал зависания определяется как signal.SIGHUP; имена переменных идентичны именам, используемым в программах на C, как указано в <signal.h>. На странице руководства Unix для „signal()“ перечислены существующие сигналы (в некоторых системах это signal(2), в других список находится в signal(7)). Обратите внимание, что не все системы определяют один и тот же набор имен сигналов; только те имена, которые определены системой, определяются этим модулем.

signal.CTRL_C_EVENT

Сигнал, соответствующий событию нажатия клавиши Ctrl+C. Этот сигнал можно использовать только с os.kill().

Availability: Окна.

Добавлено в версии 3.2.

signal.CTRL_BREAK_EVENT

Сигнал, соответствующий событию нажатия клавиши Ctrl+Break. Этот сигнал можно использовать только с os.kill().

Availability: Окна.

Добавлено в версии 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.

Добавлено в версии 3.3: Раньше эта ошибка имела подтип IOError, который теперь является псевдонимом OSError.

Модуль signal определяет следующие функции:

signal.alarm(time)

Если значение time не равно нулю, эта функция запрашивает отправку сигнала SIGALRM процессу в течение времени секунд. Любой ранее запланированный сигнал тревоги отменяется (в любой момент времени может быть запланирован только один сигнал тревоги). Возвращаемое значение представляет собой количество секунд до подачи любого ранее установленного сигнала тревоги. Если значение time равно нулю, сигнал тревоги не запланирован и все запланированные сигналы тревоги отменяются. Если возвращаемое значение равно нулю, сигнал тревоги в данный момент не запланирован.

Availability: Unix.

Смотрите справочную страницу alarm(2) для получения дополнительной информации.

signal.getsignal(signalnum)

Возвращает текущий обработчик сигнала для параметра signal 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()

Переведите процесс в спящий режим до получения сигнала; затем будет вызван соответствующий обработчик. Ничего не возвращает.

Availability: 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) для получения дополнительной информации.

Availability: Linux >= 5.1

Добавлено в версии 3.9.

signal.pthread_kill(thread_id, signalnum)

Отправьте сигнал signalnum в поток thread_id, другой поток в том же процессе, что и вызывающий. Целевой поток может выполнять любой код (на Python или нет). Однако, если целевой поток выполняет интерпретатор Python, обработчики сигналов Python будут иметь значение executed by the main thread of the main interpreter. Следовательно, единственным способом отправки сигнала конкретному потоку Python было бы принудительно завершить выполнение системного вызова с помощью InterruptedError.

Используйте threading.get_ident() или атрибут ident объектов threading.Thread, чтобы получить подходящее значение для thread_id.

Если signalnum равно 0, то сигнал не отправляется, но проверка ошибок все равно выполняется; это можно использовать для проверки того, что целевой поток все еще запущен.

Создает auditing event signal.pthread_kill с аргументами thread_id, signalnum.

Availability: Unix.

Смотрите справочную страницу pthread_kill(3) для получения дополнительной информации.

Смотрите также os.kill().

Добавлено в версии 3.3.

signal.pthread_sigmask(how, mask)

Извлеките и/или измените маску сигнала вызывающего потока. Маска сигнала - это набор сигналов, доставка которых в данный момент заблокирована для вызывающего потока. Верните старую маску сигнала в виде набора сигналов.

Поведение вызова зависит от значения параметра how следующим образом.

  • SIG_BLOCK: Набор заблокированных сигналов представляет собой объединение текущего набора и аргумента mask.

  • SIG_UNBLOCK: Сигналы в маске удаляются из текущего набора заблокированных сигналов. Допускается попытка разблокировать сигнал, который не заблокирован.

  • SIG_SETMASK: Для набора заблокированных сигналов задается значение аргумента mask.

маска - это набор номеров сигналов (например, {signal.SIGINT, signal.SIGTERM}). Используйте valid_signals() для полной маски, включающей все сигналы.

Например, signal.pthread_sigmask(signal.SIG_BLOCK, []) считывает маску сигнала вызывающего потока.

SIGKILL и SIGSTOP не могут быть заблокированы.

Availability: 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), указанный в , который срабатывает через *секунды (допускается значение с плавающей точкой, отличное от alarm()) и после этого каждые секунды (если *интервал отличен от нуля). Таймер с интервалом, указанным в , который можно сбросить, установив значение секунды равным нулю.

Когда срабатывает интервальный таймер, процессу посылается сигнал. Отправляемый сигнал зависит от используемого таймера; signal.ITIMER_REAL доставит сообщение SIGALRM, signal.ITIMER_VIRTUAL отправляет SIGVTALRM, а signal.ITIMER_PROF доставит SIGPROF.

Старые значения возвращаются в виде кортежа: (задержка, интервал).

Попытка передать таймер с недопустимым интервалом приведет к появлению ItimerError.

Availability: Unix.

signal.getitimer(which)

Возвращает текущее значение таймера с заданным интервалом, заданное параметром which.

Availability: Unix.

signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)

Задайте для дескриптора файла пробуждения значение fd. При получении сигнала номер сигнала записывается в виде одного байта в поле fd. Это может быть использовано библиотекой для запуска опроса или вызова select, что позволяет полностью обработать сигнал.

Возвращается старое значение fd при пробуждении (или -1, если активация файлового дескриптора не была включена). Если значение fd равно -1, активация файлового дескриптора отключена. Если не -1, то значение fd должно быть неблокирующим. Библиотека сама может удалить все байты из fd перед повторным вызовом poll или select.

Когда потоки включены, эта функция может быть вызвана только из the main thread of the main interpreter; попытка вызвать ее из других потоков приведет к возникновению исключения 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, в противном случае системные вызовы будут прерваны. Ничего не возвращает.

Availability: Unix.

Смотрите справочную страницу siginterrupt(3) для получения дополнительной информации.

Обратите внимание, что установка обработчика сигнала с signal() приведет к изменению режима перезапуска на прерываемый путем неявного вызова siginterrupt() со значением true flag для данного сигнала.

signal.signal(signalnum, handler)

Задайте обработчику для signal signalnum значение функции handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже) или одно из специальных значений signal.SIG_IGN или signal.SIG_DFL. Будет возвращен предыдущий обработчик сигнала (см. описание getsignal() выше). (Дополнительную информацию смотрите на странице руководства по Unix signal(2)).

Когда потоки включены, эта функция может быть вызвана только из the main thread of the main interpreter; попытка вызвать ее из других потоков приведет к возникновению исключения ValueError.

Обработчик * вызывается с двумя аргументами: номером сигнала и текущим кадром стека (None или объектом кадра; описание объектов кадра приведено в description in the type hierarchy или в описании атрибутов в inspect модуль).

В Windows signal() может быть вызван только с помощью SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM или SIGBREAK. В любом другом случае будет вызван ValueError. Обратите внимание, что не все системы определяют один и тот же набор имен сигналов; значение AttributeError будет поднято, если имя сигнала не определено как SIG* константа уровня модуля.

signal.sigpending()

Проверьте набор сигналов, ожидающих отправки в вызывающий поток (т.е. сигналы, которые были получены при блокировке). Верните набор ожидающих сигналов.

Availability: Unix.

Смотрите справочную страницу sigpending(2) для получения дополнительной информации.

Смотрите также pause(), pthread_sigmask() и sigwait().

Добавлено в версии 3.3.

signal.sigwait(sigset)

Приостановите выполнение вызывающего потока до получения одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.

Availability: 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.

Availability: Unix.

Смотрите справочную страницу sigwaitinfo(2) для получения дополнительной информации.

Смотрите также pause(), sigwait() и sigtimedwait().

Добавлено в версии 3.3.

Изменено в версии 3.5: Функция теперь прекращает работу, если прерывается сигналом, которого нет в sigset, и обработчик сигнала не генерирует исключение (объяснение смотрите в PEP 475).

signal.sigtimedwait(sigset, timeout)

Аналогично sigwaitinfo(), но принимает дополнительный аргумент timeout, указывающий время ожидания. Если значение timeout указано как 0, выполняется опрос. Возвращает None, если время ожидания истекло.

Availability: 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. Это приведет к неожиданному завершению работы вашей программы всякий раз, когда прерывается подключение к какому-либо сокету, в то время как ваша программа все еще выполняет запись в него.

Примечание об обработчиках сигналов и исключениях

Если обработчик сигнала вызывает исключение, оно передается в основной поток и может быть вызвано после выполнения любой команды bytecode. В частности, в любой момент выполнения может появиться 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...")
Вернуться на верх