signal
— Установите обработчики для асинхронных событий¶
Источник: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила¶
Функция signal.signal()
позволяет определить пользовательские обработчики, которые будут выполняться при получении сигнала. По умолчанию установлено небольшое количество обработчиков: SIGPIPE
игнорируется (поэтому ошибки записи по трубам и сокетам могут сообщаться как обычные исключения Python), а SIGINT
преобразуется в исключение KeyboardInterrupt
, если родительский процесс не изменил его.
Обработчик определенного сигнала, однажды установленный, остается установленным до тех пор, пока он не будет явно сброшен (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации), за исключением обработчика для SIGCHLD
, который следует базовой реализации.
На платформах WebAssembly сигналы эмулируются и поэтому ведут себя по-другому. Некоторые функции и сигналы недоступны на этих платформах.
Выполнение обработчиков сигналов Python¶
Обработчик сигналов Python не выполняется внутри низкоуровневого (C) обработчика сигналов. Вместо этого низкоуровневый обработчик сигналов устанавливает флаг, который указывает virtual machine на выполнение соответствующего обработчика сигналов Python в более поздний момент (например, при выполнении следующей инструкции bytecode). Это имеет свои последствия:
Не имеет смысла ловить синхронные ошибки типа
SIGFPE
илиSIGSEGV
, вызванные некорректной операцией в коде на C. Python вернется из обработчика сигнала в Си-код, который, скорее всего, снова поднимет тот же сигнал, что приведет к зависанию 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_*.Added in version 3.5.
- class signal.Handlers¶
enum.IntEnum
собирает константыSIG_DFL
иSIG_IGN
.Added in version 3.5.
- class signal.Sigmasks¶
enum.IntEnum
собирает константыSIG_BLOCK
,SIG_UNBLOCK
иSIG_SETMASK
.Availability: Unix.
Дополнительную информацию см. на страницах sigprocmask(2) и pthread_sigmask(3).
Added in version 3.5.
В модуле signal
определены следующие переменные:
- signal.SIG_DFL¶
Это один из двух стандартных вариантов обработки сигналов; он просто выполняет функцию по умолчанию для данного сигнала. Например, в большинстве систем стандартным действием для
SIGQUIT
является сброс ядра и выход, а стандартным действием дляSIGCHLD
является простое игнорирование сигнала.
- signal.SIG_IGN¶
Это еще один стандартный обработчик сигналов, который будет просто игнорировать заданный сигнал.
- signal.SIGALRM¶
Сигнал таймера с alarm(2).
Availability: Unix.
- signal.SIGBREAK¶
Прерывание с клавиатуры (CTRL + BREAK).
Availability: Windows.
- signal.SIGBUS¶
Ошибка шины (плохой доступ к памяти).
Availability: Unix.
- signal.SIGCHLD¶
Детский процесс остановлен или завершен.
Availability: Unix.
- signal.SIGCLD¶
Псевдоним для
SIGCHLD
.Availability: не macOS.
- 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).
Added in version 3.11.
- signal.SIGTERM¶
Сигнал окончания.
- signal.SIGUSR1¶
Определяемый пользователем сигнал 1.
Availability: Unix.
- signal.SIGUSR2¶
Определяемый пользователем сигнал 2.
Availability: Unix.
- signal.SIGWINCH¶
Сигнал изменения размера окна.
Availability: Unix.
- SIG*
Все номера сигналов задаются символически. Например, сигнал зависания определяется как
signal.SIGHUP
; имена переменных идентичны именам, используемым в программах на языке C, как в<signal.h>
. На man-странице Unix для „signal()
приведен список существующих сигналов (в некоторых системах это signal(2), в других список находится в signal(7)). Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; в этом модуле определяются только те имена, которые определены системой.
- signal.CTRL_C_EVENT¶
Сигнал, соответствующий событию нажатия клавиши Ctrl+C. Этот сигнал можно использовать только с
os.kill()
.Availability: Windows.
Added in version 3.2.
- signal.CTRL_BREAK_EVENT¶
Сигнал, соответствующий событию нажатия клавиши Ctrl+Break. Этот сигнал можно использовать только с
os.kill()
.Availability: Windows.
Added in version 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()
, указывающее на то, что сигналы должны быть заблокированы.Added in version 3.3.
- signal.SIG_UNBLOCK¶
Возможное значение параметра how -
pthread_sigmask()
, указывающее на то, что сигналы должны быть разблокированы.Added in version 3.3.
- signal.SIG_SETMASK¶
Возможное значение параметра how -
pthread_sigmask()
, указывающее на то, что маска сигнала должна быть заменена.Added in version 3.3.
Модуль signal
определяет одно исключение:
- exception signal.ItimerError¶
Вызывается для сигнализации об ошибке базовой реализации
setitimer()
илиgetitimer()
. Ожидайте эту ошибку, если вsetitimer()
передан недействительный интервальный таймер или отрицательное время. Эта ошибка является подтипомOSError
.
Модуль signal
определяет следующие функции:
- signal.alarm(time)¶
Если time ненулевое, эта функция запрашивает отправку процессу сигнала
SIGALRM
через time секунд. Любой ранее запланированный сигнал отменяется (в любой момент времени может быть запланирован только один сигнал). Возвращаемое значение - это количество секунд до того, как должен был быть подан любой ранее установленный сигнал. Если time равно нулю, сигнал тревоги не запланирован, а любой запланированный сигнал отменяется. Если возвращаемое значение равно нулю, то ни один сигнал тревоги в данный момент не запланирован.Availability: 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 недопустим.Added in version 3.8.
- signal.valid_signals()¶
Возвращает набор допустимых номеров сигналов на данной платформе. Он может быть меньше
range(1, NSIG)
, если некоторые сигналы зарезервированы системой для внутреннего использования.Added in version 3.8.
- signal.pause()¶
Вызывает процесс, который будет спать до тех пор, пока не будет получен сигнал; затем будет вызван соответствующий обработчик. Не возвращает ничего.
Availability: Unix.
Дополнительную информацию см. на странице руководства signal(2).
См. также
sigwait()
,sigwaitinfo()
,sigtimedwait()
иsigpending()
.
- signal.raise_signal(signum)¶
Посылает сигнал вызывающему процессу. Ничего не возвращает.
Added in version 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
Added in version 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()
.Added in version 3.3.
- signal.pthread_sigmask(how, mask)¶
Получение и/или изменение маски сигналов вызывающего потока. Маска сигнала - это набор сигналов, доставка которых в данный момент заблокирована для вызывающего потока. Возвращает старую маску сигналов в виде набора сигналов.
Поведение вызова зависит от значения параметра how, как показано ниже.
SIG_BLOCK
: Набор заблокированных сигналов представляет собой объединение текущего набора и аргумента маска.SIG_UNBLOCK
: Сигналы в mask удаляются из текущего набора заблокированных сигналов. Допускается попытка разблокировать сигнал, который не заблокирован.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()
.Added in version 3.3.
- signal.setitimer(which, seconds, interval=0.0)¶
Устанавливает заданный интервальный таймер (один из
signal.ITIMER_REAL
,signal.ITIMER_VIRTUAL
илиsignal.ITIMER_PROF
), указанный which, на срабатывание через секунды (принимается плавающая величина, отличная отalarm()
) и после этого каждые интервал секунд (если интервал ненулевой). Интервальный таймер, указанный which, может быть очищен установкой seconds в ноль.Когда срабатывает интервальный таймер, процессу посылается сигнал. Отправляемый сигнал зависит от используемого таймера;
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 при потере сигналов.При втором подходе мы используем wakeup 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()
с истинным значением флага для данного сигнала.
- signal.signal(signalnum, handler)¶
Установите обработчик сигнала signalnum в функцию handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже), или одним из специальных значений
signal.SIG_IGN
илиsignal.SIG_DFL
. Будет возвращен предыдущий обработчик сигнала (см. описаниеgetsignal()
выше). (Дополнительную информацию см. на man-странице 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
. Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; если имя сигнала не определено как константа уровня модуляSIG*
, будет выдан сигналAttributeError
.
- signal.sigpending()¶
Изучите набор сигналов, которые ожидают доставки в вызывающий поток (т.е. сигналы, которые были подняты во время блокировки). Возвращает набор ожидающих сигналов.
Availability: Unix.
Дополнительную информацию см. на странице руководства sigpending(2).
См. также
pause()
,pthread_sigmask()
иsigwait()
.Added in version 3.3.
- signal.sigwait(sigset)¶
Приостанавливает выполнение вызывающего потока до получения одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.
Availability: Unix.
Дополнительную информацию см. на странице руководства sigwait(3).
См. также
pause()
,pthread_sigmask()
,sigpending()
,sigwaitinfo()
иsigtimedwait()
.Added in version 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()
.Added in version 3.3.
Изменено в версии 3.5: Теперь функция выполняется повторно, если она прерывается сигналом, не входящим в sigset, и обработчик сигнала не вызывает исключения (см. обоснование в PEP 475).
- signal.sigtimedwait(sigset, timeout)¶
Аналогично
sigwaitinfo()
, но принимает дополнительный аргумент timeout, задающий тайм-аут. Если timeout указан как0
, выполняется опрос. Возвращает значениеNone
, если таймаут наступил.Availability: Unix.
Дополнительную информацию см. на странице руководства sigtimedwait(2).
См. также
pause()
,sigwait()
иsigwaitinfo()
.Added in version 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...")