Spec-Zone.ru › Python 3.13

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: связанные с 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.

Добавлено в версии 3.3: Раньше это исключение было подклассом IOError, который теперь является псевдонимом 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-time, уровень API 31

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

signal.pthread_kill(thread_id, signalnum)

Отправляет сигнал signalnum потоку thread_id — другому потоку того же процесса, что и вызывающий поток. Целевой поток может выполнять любой код (на Python или другом языке). Однако если целевой поток выполняет интерпретатор Python, обработчики сигналов Python будут выполнены главным потоком главного интерпретатора. Поэтому единственная цель отправки сигнала конкретному потоку Python — вызвать сбой выполняемого системного вызова с исключением InterruptedError.

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

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

Возбуждает событие аудита signal.pthread_kill с аргументами thread_id, signalnum.

Доступность: Unix.

Дополнительные сведения см. на странице руководства pthread_kill(3).

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

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

signal.pthread_sigmask(how, mask)

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

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

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

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

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

SIGKILL и SIGSTOP нельзя заблокировать.

Доступность: Unix.

Дополнительные сведения см. на страницах руководства sigprocmask(2) и pthread_sigmask(3).

См. также pause(), sigpending() и sigwait().

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

signal.setitimer(which, seconds, interval=0.0)

Устанавливает указанный интервальный таймер (один из signal.ITIMER_REAL, signal.ITIMER_VIRTUAL или signal.ITIMER_PROF), заданный параметром which, так, чтобы он сработал через seconds (допускается значение с плавающей точкой, в отличие от alarm()), а затем срабатывал через каждые interval секунд (если interval не равен нулю). Указанный параметром which интервальный таймер можно отключить, задав значение seconds, равное нулю.

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

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

Передача недопустимого интервального таймера приводит к возбуждению исключения ItimerError.

Доступность: Unix.

signal.getitimer(which)

Возвращает текущее значение заданного интервального таймера which.

Доступность: Unix.

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

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

Возвращается старый дескриптор пробуждения (или -1, если пробуждение через файловый дескриптор не было включено). Если fd равен -1, пробуждение через файловый дескриптор отключается. Если 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)

Устанавливает функцию handler в качестве обработчика сигнала signalnum. 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: Теперь функция повторяет попытку с пересчитанным значением 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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/signal.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API