Spec-Zone.ru › Python 3.12

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

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

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

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

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

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

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

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

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

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

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

Обработчики сигналов Python всегда выполняются в главном потоке Python основного интерпретатора, даже если сигнал был получен в другом потоке. Это означает, что сигналы нельзя использовать для межпоточного взаимодействия. Вместо этого можно использовать примитивы синхронизации из модуля threading.

Кроме того, устанавливать новый обработчик сигнала может только главный поток основного интерпретатора.

Содержимое модуля

Изменено в версии 3.5: связанные константы signal (SIG*), handler (SIG_DFL, SIG_IGN) и sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK), перечисленные ниже, были преобразованы в enums (Signals, Handlers и Sigmasks соответственно). Функции getsignal(), pthread_sigmask(), sigpending() и sigwait() возвращают удобочитаемые значения enums в виде объектов Signals.

Модуль signal определяет три перечисления:

class signal.Signals

Коллекция enum.IntEnum констант SIG* и констант CTRL_*.

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

class signal.Handlers

Коллекция enum.IntEnum констант SIG_DFL и SIG_IGN.

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

class signal.Sigmasks

Коллекция enum.IntEnum констант SIG_BLOCK, SIG_UNBLOCK и SIG_SETMASK.

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

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

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

В модуле signal определены следующие переменные:

signal.SIG_DFL

Это один из двух стандартных вариантов обработки сигналов; он просто выполнит действие по умолчанию для сигнала. Например, в большинстве систем действие по умолчанию для SIGQUIT — создать дамп памяти и завершить процесс, тогда как действие по умолчанию для SIGCHLD — просто игнорировать его.

signal.SIG_IGN

Это другой стандартный обработчик сигналов, который просто игнорирует переданный сигнал.

signal.SIGABRT

Сигнал прерывания от abort(3).

signal.SIGALRM

Сигнал таймера от alarm(2).

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

signal.SIGBREAK

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

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

signal.SIGBUS

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

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

signal.SIGCHLD

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

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

signal.SIGCLD

Псевдоним SIGCHLD.

Доступность: не macOS.

signal.SIGCONT

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

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

signal.SIGFPE

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

См. также

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

signal.SIGHUP

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

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

signal.SIGILL

Недопустимая инструкция.

signal.SIGINT

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

Действие по умолчанию — вызвать исключение KeyboardInterrupt.

signal.SIGKILL

Сигнал завершения процесса.

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

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

signal.SIGPIPE

Разрыв канала: запись в канал, у которого нет читателей.

Действие по умолчанию — игнорировать сигнал.

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

signal.SIGSEGV

Ошибка сегментации: недопустимая ссылка на память.

signal.SIGSTKFLT

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

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

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

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

signal.SIGTERM

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

signal.SIGUSR1

Пользовательский сигнал 1.

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

signal.SIGUSR2

Пользовательский сигнал 2.

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

signal.SIGWINCH

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

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

SIG*

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

signal.CTRL_C_EVENT

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

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

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

signal.CTRL_BREAK_EVENT

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

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

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

signal.NSIG

Число, на единицу превышающее номер наибольшего сигнала. Используйте valid_signals(), чтобы получить допустимые номера сигналов.

signal.ITIMER_REAL

Уменьшает значение интервального таймера в реальном времени и по истечении срока отправляет SIGALRM.

signal.ITIMER_VIRTUAL

Уменьшает значение интервального таймера только во время выполнения процесса и по истечении срока отправляет SIGVTALRM.

signal.ITIMER_PROF

Уменьшает значение интервального таймера как во время выполнения процесса, так и во время работы системы от имени процесса. В сочетании с ITIMER_VIRTUAL этот таймер обычно используется для профилирования времени, затрачиваемого приложением в пользовательском пространстве и пространстве ядра. По истечении срока отправляется SIGPROF.

signal.SIG_BLOCK

Одно из возможных значений параметра how функции pthread_sigmask(), указывающее, что сигналы необходимо блокировать.

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

signal.SIG_UNBLOCK

Одно из возможных значений параметра how функции pthread_sigmask(), указывающее, что блокировку сигналов необходимо снять.

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

signal.SIG_SETMASK

Одно из возможных значений параметра how функции pthread_sigmask(), указывающее, что маску сигналов необходимо заменить.

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

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

exception signal.ItimerError

Возникает, чтобы сообщить об ошибке в реализации setitimer() или getitimer(). Это исключение возникает, если в setitimer() передан недопустимый интервальный таймер или отрицательное время. Это исключение является подклассом OSError.

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

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

signal.pthread_kill(thread_id, signalnum)

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

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

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

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

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

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

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

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

signal.pthread_sigmask(how, mask)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

signal.getitimer(which)

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

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

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

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

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

Если потоки включены, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из другого потока приведёт к возникновению исключения ValueError.

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

В первом случае данные считываются из буфера файлового дескриптора, а значения байтов соответствуют номерам сигналов. Это просто, но в редких случаях может возникнуть проблема: обычно объём буфера файлового дескриптора ограничен, и если слишком много сигналов поступит за короткое время, буфер может переполниться, из-за чего некоторые сигналы будут потеряны. При использовании этого способа следует задать warn_on_full_buffer=True, чтобы при потере сигналов по крайней мере выводилось предупреждение в stderr.

Во втором случае файловый дескриптор пробуждения используется только для пробуждения, а фактические значения байтов игнорируются. В этом случае важно лишь, пуст ли буфер файлового дескриптора; переполнение буфера не указывает на проблему. При использовании этого способа следует задать warn_on_full_buffer=False, чтобы не вводить пользователей в заблуждение ложными предупреждениями.

Изменено в версии 3.5: В Windows функция теперь также поддерживает дескрипторы сокетов.

Изменено в версии 3.7: Добавлен параметр warn_on_full_buffer.

signal.siginterrupt(signalnum, flag)

Изменяет поведение перезапуска системных вызовов: если flag равен False, системные вызовы будут перезапускаться при прерывании сигналом signalnum; в противном случае системные вызовы будут прерываться. Ничего не возвращает.

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

Дополнительные сведения см. на справочной странице siginterrupt(3).

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

signal.signal(signalnum, handler)

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

Если потоки включены, эту функцию можно вызывать только из главного потока главного интерпретатора; попытка вызвать её из другого потока приведёт к возникновению исключения ValueError.

Обработчик handler вызывается с двумя аргументами: номером сигнала и текущим стековым кадром (None или объектом кадра; описание объектов кадра см. в описании в иерархии типов или в описаниях атрибутов модуля inspect).

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

signal.sigpending()

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

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

Дополнительные сведения см. на справочной странице sigpending(2).

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

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

signal.sigwait(sigset)

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

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

Дополнительные сведения см. на справочной странице sigwait(3).

См. также pause(), pthread_sigmask(), sigpending(), sigwaitinfo() и sigtimedwait().

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

signal.sigwaitinfo(sigset)

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

Возвращаемое значение — объект, представляющий данные структуры siginfo_t, а именно: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band.

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

Дополнительные сведения см. на справочной странице sigwaitinfo(2).

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

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

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

signal.sigtimedwait(sigset, timeout)

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

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

Дополнительные сведения см. на справочной странице sigtimedwait(2).

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

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

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

Примеры

Ниже приведён минимальный пример программы. Она использует функцию alarm(), чтобы ограничить время ожидания открытия файла; это полезно, если файл относится к последовательному устройству, которое может быть выключено. В таком случае вызов os.open() обычно будет бесконечно ждать. Решение состоит в том, чтобы установить 5-секундный таймер перед открытием файла; если операция займёт слишком много времени, будет отправлен сигнал таймера, и обработчик вызовет исключение.

import signal, os

def handler(signum, frame):
    signame = signal.Signals(signum).name
    print(f'Signal handler called with signal {signame} ({signum})')
    raise OSError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm

Примечание о SIGPIPE

Перенаправление вывода программы в такие инструменты, как head(1), приведёт к отправке процессу сигнала SIGPIPE, когда получатель стандартного вывода преждевременно закроет соединение. Это приводит к исключению, например BrokenPipeError: [Errno 32] Broken pipe. Чтобы обработать такой случай, оберните точку входа программы и перехватите это исключение следующим образом:

import os
import sys

def main():
    try:
        # simulate large output (your code replaces this loop)
        for x in range(10000):
            print("y")
        # flush output here to force SIGPIPE to be triggered
        # while inside this try block.
        sys.stdout.flush()
    except BrokenPipeError:
        # Python flushes standard streams on exit; redirect remaining output
        # to devnull to avoid another BrokenPipeError at shutdown
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)  # Python exits with error code 1 on EPIPE

if __name__ == '__main__':
    main()

Не задавайте для сигнала SIGPIPE действие SIG_DFL, чтобы избежать исключения BrokenPipeError. В противном случае программа будет неожиданно завершаться при любом разрыве соединения с сокетом, если в этот момент она продолжает в него записывать.

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

Если обработчик сигнала вызывает исключение, оно передаётся главному потоку и может возникнуть после любой инструкции байт-кода. В частности, исключение KeyboardInterrupt может возникнуть в любой момент выполнения программы. Большинство кода Python, в том числе стандартная библиотека, невозможно сделать устойчивым к этому, поэтому исключение KeyboardInterrupt (или любое другое исключение, вызванное обработчиком сигнала) в редких случаях может привести программу в неожиданное состояние.

Рассмотрим следующий код, чтобы проиллюстрировать эту проблему:

class SpamContext:
    def __init__(self):
        self.lock = threading.Lock()

    def __enter__(self):
        # If KeyboardInterrupt occurs here, everything is fine
        self.lock.acquire()
        # If KeyboardInterrupt occurs here, __exit__ will not be called
        ...
        # KeyboardInterrupt could occur just before the function returns

    def __exit__(self, exc_type, exc_val, exc_tb):
        ...
        self.lock.release()

Для многих программ, особенно тех, которые должны лишь завершаться при возникновении KeyboardInterrupt, это не проблема. Однако сложным приложениям и приложениям, требующим высокой надёжности, следует избегать вызова исключений из обработчиков сигналов. Также им следует избегать перехвата KeyboardInterrupt в качестве способа корректного завершения работы. Вместо этого им следует установить собственный обработчик SIGINT. Ниже приведён пример HTTP-сервера, который позволяет избежать KeyboardInterrupt:

import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler

interrupt_read, interrupt_write = socket.socketpair()

def handler(signum, frame):
    print('Signal handler called with signal', signum)
    interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)

def serve_forever(httpd):
    sel = DefaultSelector()
    sel.register(interrupt_read, EVENT_READ)
    sel.register(httpd, EVENT_READ)

    while True:
        for key, _ in sel.select():
            if key.fileobj == interrupt_read:
                interrupt_read.recv(1)
                return
            if key.fileobj == httpd:
                httpd.handle_request()

print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/signal.html

Spec-Zone.ru

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