Spec-Zone.ru › Python 3.12

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

Исходный код: 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: сигналы (SIG*), обработчики (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.

END_OF_DOCUMENT_MARKER
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.

END_OF_DOCUMENT_MARKER
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.

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

Попытка передачи недопустимого таймера с интервалом приведет к ошибке 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.12/library/signal.html

Spec-Zone.ru

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