signal — Define manipuladores para eventos assíncronos

Código-fonte: Lib/signal.py


Este módulo fornece mecanismos para usar manipuladores de sinal em Python.

Regras gerais

A função signal.signal() permite definir manipuladores personalizados a serem executados quando um sinal é recebido. Um pequeno número de manipuladores padrão são instalados: SIGPIPE é ignorado (então erros de gravação em encadeamentos e sockets podem ser relatados como exceções comuns do Python) e SIGINT é traduzido em uma exceção KeyboardInterrupt se o processo pai não a tiver alterado.

Um manipulador para um sinal específico, uma vez definido, permanece instalado até ser explicitamente redefinido (o Python emula a interface de estilo BSD independentemente da implementação subjacente), com exceção do manipulador para SIGCHLD, que segue a implementação subjacente.

Em plataformas WebAssembly, os sinais são emulados e, portanto, se comportam de forma diferente. Várias funções e sinais não estão disponíveis nessas plataformas.

Execução de manipuladores de sinais Python

Um manipulador de sinal Python não é executado dentro do manipulador de sinal de baixo nível (C). Em vez disso, o manipulador de sinal de baixo nível define um sinalizador que diz à máquina virtual para executar o manipulador de sinal Python correspondente em um ponto posterior (por exemplo, na próxima instrução bytecode). Isso tem consequências:

  • Faz pouco sentido capturar erros síncronos como SIGFPE ou SIGSEGV que são causados por uma operação inválida no código C. O Python retornará do manipulador de sinais para o código C, o que provavelmente levantará o mesmo sinal novamente, fazendo com que o Python aparentemente trave. Do Python 3.3 em diante, você pode usar o módulo faulthandler para relatar erros síncronos.

  • Um cálculo de longa duração implementado puramente em C (como correspondência de expressão regular em um grande corpo de texto) pode ser executado ininterruptamente por um período de tempo arbitrário, independentemente de quaisquer sinais recebidos. Os manipuladores de sinal do Python serão chamados quando o cálculo terminar.

  • Se o manipulador levantar uma exceção, ela será levantada “do nada” na thread principal. Veja a nota abaixo para uma discussão.

Sinais e threads

Os manipuladores de sinais Python são sempre executados na thread principal do Python do interpretador principal, mesmo se o sinal foi recebido em outra thread. Isso significa que os sinais não podem ser usados como um meio de comunicação entre threads. Você pode usar as primitivas de sincronização do módulo threading em vez disso.

Além disso, somente a thread principal do interpretador principal tem permissão para definir um novo manipulador de sinal.

Conteúdo do módulo

Alterado na versão 3.5: As constantes relacionadas a sinal (SIG*), manipulador (SIG_DFL, SIG_IGN) e sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) listadas abaixo foram transformadas em enums (Signals, Handlers e Sigmasks respectivamente). As funções getsignal(), pthread_sigmask(), sigpending() e sigwait() retornam enums legíveis por humanos como objetos Signals.

O módulo de sinal define três enumerações:

class signal.Signals

Coleção de enum.IntEnum de constantes SIG* e constantes CTRL_*.

Adicionado na versão 3.5.

class signal.Handlers

Coleção de enum.IntEnum das constantes SIG_DFL e SIG_IGN.

Adicionado na versão 3.5.

class signal.Sigmasks

Coleção de enum.IntEnum das constantes SIG_BLOCK, SIG_UNBLOCK e SIG_SETMASK.

Disponibilidade: Unix.

Veja a página man sigprocmask(2) e pthread_sigmask(3) para mais informações.

Adicionado na versão 3.5.

As variáveis definidas no módulo signal são:

signal.SIG_DFL

Esta é uma das duas opções de manipulação de sinal padrão; ela simplesmente executará a função padrão para o sinal. Por exemplo, na maioria dos sistemas, a ação padrão para SIGQUIT é despejar o núcleo e sair, enquanto a ação padrão para SIGCHLD é simplesmente ignorá-lo.

signal.SIG_IGN

Este é outro manipulador de sinal padrão, que simplesmente ignorará o sinal fornecido.

signal.SIGABRT

Sinal de abortar de abort(3).

signal.SIGALRM

Sinal do temporizador de alarm(2).

signal.SIGBREAK

Interrupção do teclado (CTRL + BREAK).

Disponibilidade: Windows.

signal.SIGBUS

Erro de barramento (acesso incorreto à memória).

signal.SIGCHLD

Processo filho interrompido ou encerrado.

signal.SIGCLD

Apelido para SIGCHLD.

Disponibilidade: not macOS.

signal.SIGCONT

Continua o processo se ele estiver parado no momento

signal.SIGFPE

Exceção de ponto flutuante. Por exemplo, divisão por zero.

Ver também

ZeroDivisionError é levatanda quando o segundo argumento de uma operação de divisão ou módulo é zero.

signal.SIGHUP

Travamento detectado no terminal de controle ou morte do processo de controle.

signal.SIGILL

Instrução ilegal.

signal.SIGINT

Interrupção do teclado (CTRL + C).

A ação padrão é levantar KeyboardInterrupt.

signal.SIGKILL

Sinal de matar.

Ele não pode ser capturado, bloqueado ou ignorado.

signal.SIGPIPE

Encadeamento quebrado: escreve no encadeamento sem leitores.

A ação padrão é ignorar o sinal.

signal.SIGSEGV

Falha de segmentação: referência de memória inválida.

signal.SIGSTKFLT

Falha de pilha no coprocessador. O kernel Linux não levanta esse sinal: ele só pode ser emitido no espaço do usuário.

Disponibilidade: Linux.

Em arquiteturas onde o sinal está disponível. Veja a página man signal(7) para mais informações.

Adicionado na versão 3.11.

signal.SIGTERM

Sinal de encerramento.

signal.SIGUSR1

Sinal definido pelo usuário 1.

signal.SIGUSR2

Sinal definido pelo usuário 2.

signal.SIGWINCH

Sinal de redimensionamento do Windows.

SIG*

Todos os números de sinal são definidos simbolicamente. Por exemplo, o sinal de desligamento é definido como signal.SIGHUP; os nomes das variáveis ​​são idênticos aos usados ​​em programas C, como encontrados em <signal.h>. A página man do Unix para ‘signal()’ lista os sinais existentes (em alguns sistemas, é signal(2), em outros, a lista está em signal(7)). Observe que nem todos os sistemas definem o mesmo conjunto de nomes de sinais; apenas os nomes definidos pelo sistema são definidos por este módulo.

signal.CTRL_C_EVENT

O sinal correspondente ao evento de pressionamento de tecla Ctrl+C. Este sinal só pode ser usado com os.kill().

Disponibilidade: Windows.

Adicionado na versão 3.2.

signal.CTRL_BREAK_EVENT

O sinal correspondente ao evento de pressionamento de tecla Ctrl+Break. Este sinal só pode ser usado com os.kill().

Disponibilidade: Windows.

Adicionado na versão 3.2.

signal.NSIG

Um a mais que o número do sinal mais alto. Use valid_signals() para obter números de sinais válidos.

signal.ITIMER_REAL

Diminui o intervalo do temporizador em tempo real e entrega SIGALRM ao expirar.

signal.ITIMER_VIRTUAL

Diminui o intervalo do temporizador somente quando o processo está em execução e entrega SIGVTALRM após a expiração.

signal.ITIMER_PROF

Diminui o temporizador de intervalo tanto quando o processo é executado quanto quando o sistema está executando em nome do processo. Juntamente com o ITIMER_VIRTUAL, este temporizador é geralmente usado para criar um perfil do tempo gasto pelo aplicativo nos espaços do usuário e do kernel. O SIGPROF é fornecido após a expiração.

signal.SIG_BLOCK

Um possível valor para o parâmetro how para pthread_sigmask() indicando que os sinais devem ser bloqueados.

Adicionado na versão 3.3.

signal.SIG_UNBLOCK

Um possível valor para o parâmetro how para pthread_sigmask() indicando que os sinais devem ser desbloqueados.

Adicionado na versão 3.3.

signal.SIG_SETMASK

Um possível valor para o parâmetro how para pthread_sigmask() indicando que a máscara de sinal deve ser substituída.

Adicionado na versão 3.3.

O módulo signal define uma exceção:

exception signal.ItimerError

Levantada para sinalizar um erro da implementação subjacente de setitimer() ou getitimer(). Espere este erro se um temporizador de intervalo inválido ou um tempo negativo for passado para setitimer(). Este erro é um subtipo de OSError.

Adicionado na versão 3.3: Este erro costumava ser um subtipo de IOError, que agora é um apelido de OSError.

O módulo signal define as seguintes funções:

signal.alarm(time)

Se time for diferente de zero, esta função solicita que um sinal SIGALRM seja enviado ao processo em time segundos. Qualquer alarme previamente agendado será cancelado (apenas um alarme pode ser agendado por vez). O valor retornado será o número de segundos antes de qualquer alarme previamente configurado ter sido emitido. Se time for zero, nenhum alarme será agendado e qualquer alarme agendado será cancelado. Se o valor retornado for zero, nenhum alarme será agendado no momento.

Disponibilidade: Unix.

Veja a página man alarm(2) para mais informações.

signal.getsignal(signalnum)

Retorna o manipulador de sinal atual para o sinal signalnum. O valor retornado pode ser um objeto Python invocável ou um dos valores especiais signal.SIG_IGN, signal.SIG_DFL ou None. Aqui, signal.SIG_IGN significa que o sinal foi ignorado anteriormente, signal.SIG_DFL significa que a maneira padrão de manipular o sinal estava em uso anteriormente e None significa que o manipulador de sinal anterior não foi instalado a partir do Python.

signal.strsignal(signalnum)

Retorna a descrição do sinal signalnum, como “Interrupt” para SIGINT. Retorna None se signalnum não tiver descrição. Levanta ValueError se signalnum for inválido.

Adicionado na versão 3.8.

signal.valid_signals()

Retorna o conjunto de números de sinais válidos nesta plataforma. Pode ser menor que range(1, NSIG) se alguns sinais forem reservados pelo sistema para uso interno.

Adicionado na versão 3.8.

signal.pause()

Faz o processo hibernar até que um sinal seja recebido; o manipulador apropriado será então chamado. Não retorna nada.

Disponibilidade: Unix.

Veja a página man signal(2) para mais informações.

veja também sigwait(), sigwaitinfo(), sigtimedwait() e sigpending().

signal.raise_signal(signum)

Envia um sinal para o processo de chamada. Não retorna nada.

Adicionado na versão 3.8.

signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)

Envia o sinal sig para o processo referenciado pelo descritor de arquivo pidfd. Atualmente, o Python não suporta o parâmetro siginfo; ele deve ser None. O argumento flags é fornecido para futuras extensões; nenhum valor de sinalizador está definido no momento.

Veja a página man pidfd_send_signal(2) para mais informações.

Disponibilidade: Linux >= 5.1, Android >= build-time API level 31

Adicionado na versão 3.9.

signal.pthread_kill(thread_id, signalnum)

Envia o sinal signalnum para a thread thread_id, outra thread no mesmo processo que a chamadora. A thread alvo pode estar executando qualquer código (Python ou não). No entanto, se a thread alvo estiver executando o interpretador Python, os manipuladores de sinal Python serão executados pela thread principal do interpretador principal. Portanto, o único objetivo de enviar um sinal para uma thread Python específica seria forçar uma chamada de sistema em execução a falhar com InterruptedError.

Utilize a threading.get_ident() ou o atributo ident dos objetos threading.Thread para obter um valor adequado para thread_id.

Se signalnum for 0, nenhum sinal será enviado, mas a verificação de erros ainda será realizada; isso pode ser usado para verificar se a thread de destino ainda está em execução.

Levanta um evento de auditoria signal.pthread_kill com os argumentos thread_id, signalnum.

Disponibilidade: Unix.

Veja a página man pthread_kill(3) para mais informações.

Veja também os.kill().

Adicionado na versão 3.3.

signal.pthread_sigmask(how, mask)

Busca e/ou altera a máscara de sinal da thread chamadora. A máscara de sinal é o conjunto de sinais cuja entrega está atualmente bloqueada para o chamador. Retorna a máscara de sinal antiga como um conjunto de sinais.

O comportamento da chamada depende do valor de how, como segue.

  • SIG_BLOCK: O conjunto de sinais bloqueados é a união do conjunto atual e do argumento mask.

  • SIG_UNBLOCK: Os sinais em mask são removidos do conjunto atual de sinais bloqueados. É permitido tentar desbloquear um sinal que não esteja bloqueado.

  • SIG_SETMASK: O conjunto de sinais bloqueados é definido como o argumento mask.

mask é um conjunto de números de sinais (ex.: {signal.SIGINT, signal.SIGTERM}). Use valid_signals() para uma máscara completa, incluindo todos os sinais.

Por exemplo, signal.pthread_sigmask(signal.SIG_BLOCK, []) lê a máscara de sinal da thread de chamada.

SIGKILL e SIGSTOP não podem ser bloqueados.

Disponibilidade: Unix.

Veja a página man sigprocmask(2) e pthread_sigmask(3) para mais informações.

Veja também pause(), sigpending() e sigwait().

Adicionado na versão 3.3.

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

Define o temporizador de intervalo fornecido (um dos seguintes: signal.ITIMER_REAL, signal.ITIMER_VIRTUAL ou signal.ITIMER_PROF) especificado por which para disparar após seconds (float é aceito, diferente de alarm()) e, depois disso, a cada interval segundos (se interval for diferente de zero). O temporizador de intervalo especificado por which pode ser zerado definindo seconds como zero.

Quando um temporizador de intervalo dispara, um sinal é enviado ao processo. O sinal enviado depende do temporizador utilizado; signal.ITIMER_REAL enviará SIGALRM, signal.ITIMER_VIRTUAL enviará SIGVTALRM e signal.ITIMER_PROF enviará SIGPROF.

Os valores antigos são retornados como uma tupla: (atraso, intervalo).

Tentar passar um intervalo de tempo inválido causará uma ItimerError.

signal.getitimer(which)

Retorna o valor atual de um intervalo de tempo especificado por which.

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

Define o descritor de arquivo de ativação como fd. Quando um sinal para o qual seu programa registrou um manipulador de sinais é recebido, o número do sinal é escrito como um único byte no fd. Se você não registrou um manipulador de sinais para os sinais de seu interesse, nada será escrito no fd de ativação. Isso pode ser usado por uma biblioteca para ativar uma chamada de poll ou seleção, permitindo que o sinal seja totalmente processado.

O antigo fd de ativação é retornado (ou -1 se a ativação do descritor de arquivo não estava habilitada). Se fd for -1, a ativação do descritor de arquivo está desabilitada. Se não for -1, fd deve ser não bloqueante. Cabe à biblioteca remover quaisquer bytes de fd antes de chamar poll ou select novamente.

Quando threads estão habilitadas, esta função só pode ser chamada da thread principal do interpretador principal; tentar chamá-la de outras threads levantará uma exceção ValueError.

Há duas maneiras comuns de usar esta função. Em ambas as abordagens, você usa o fd para despertar quando um sinal chega, mas elas diferem na forma como determinam which sinal ou sinais chegaram.

Na primeira abordagem, lemos os dados do buffer do fd, e os valores dos bytes fornecem os números dos sinais. Isso é simples, mas em casos raros pode apresentar um problema: geralmente, o fd terá um espaço de buffer limitado e, se muitos sinais chegarem muito rápido, o buffer pode ficar cheio e alguns sinais podem ser perdidos. Se você usar essa abordagem, defina warn_on_full_buffer=True, o que pelo menos fará com que um aviso seja impresso no stderr quando os sinais forem perdidos.

Na segunda abordagem, usamos o fd de ativação apenas para ativações e ignoramos os valores de bytes reais. Nesse caso, tudo o que importa é se o buffer do fd está vazio ou não; um buffer cheio não indica nenhum problema. Se você usar essa abordagem, defina warn_on_full_buffer=False para que seus usuários não sejam confundidos por mensagens de aviso falsas.

Alterado na versão 3.5: No Windows, a função agora também suporta manipuladores de socket.

Alterado na versão 3.7: Adiciona o parâmetro warn_on_full_buffer.

signal.siginterrupt(signalnum, flag)

Altera o comportamento de reinicialização de chamadas de sistema: se flag for False, as chamadas de sistema serão reiniciadas quando interrompidas pelo sinal signalnum; caso contrário, as chamadas de sistema serão interrompidas. Não retorna nada.

Disponibilidade: Unix.

Veja a página man siginterrupt(3) para mais informações.

Observe que a instalação de um manipulador de sinal com signal() redefinirá o comportamento de reinicialização para interrompível ao chamar implicitamente siginterrupt() com flag contendo valor verdadeiro para o sinal fornecido.

signal.signal(signalnum, handler)

Define o manipulador do sinal signalnum para a função handler. handler pode ser um objeto Python invocável que recebe dois argumentos (veja abaixo), ou um dos valores especiais signal.SIG_IGN ou signal.SIG_DFL. O manipulador de sinal anterior será retornado (veja a descrição de getsignal() acima). (Consulte a página man do Unix signal(2) para mais informações.)

Quando threads estão habilitadas, esta função só pode ser chamada da thread principal do interpretador principal; tentar chamá-la de outras threads levantará uma exceção ValueError.

O handler é chamado com dois argumentos: o número do sinal e o quadro de pilha atual (None ou um objeto quadro; para uma descrição dos objetos quadro, consulte a descrição na hierarquia de tipo ou consulte as descrições de atributo no módulo inspect).

No Windows, signal() só pode ser chamado com SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM ou SIGBREAK. Uma exceção ValueError será levantada em qualquer outro caso. Observe que nem todos os sistemas definem o mesmo conjunto de nomes de sinais; uma exceção AttributeError será levantada se um nome de sinal não for definido como constante de nível de módulo SIG*.

signal.sigpending()

Examine o conjunto de sinais pendentes para entrega ao thread de chamada (ou seja, os sinais que foram gerados enquanto bloqueados). Retorne o conjunto de sinais pendentes.

Disponibilidade: Unix.

Veja a página man sigpending(2) para mais informações.

Veja também pause(), pthread_sigmask() e sigwait().

Adicionado na versão 3.3.

signal.sigwait(sigset)

Suspende a execução do thread de chamada até a entrega de um dos sinais especificados no conjunto de sinais sigset. A função aceita o sinal (remove-o da lista de sinais pendentes) e retorna o número do sinal.

Disponibilidade: Unix.

Veja a página man sigwait(3) para mais informações.

Veja também pause(), pthread_sigmask(), sigpending(), sigwaitinfo() e sigtimedwait().

Adicionado na versão 3.3.

signal.sigwaitinfo(sigset)

Suspende a execução da thread chamadora até a entrega de um dos sinais especificados no conjunto de sinais sigset. A função aceita o sinal e o remove da lista de sinais pendentes. Se um dos sinais em sigset já estiver pendente para a thread chamadora, a função retornará imediatamente com informações sobre esse sinal. O manipulador de sinais não é chamado para o sinal entregue. A função levanta InterruptedError se for interrompida por um sinal que não esteja em sigset.

O valor de retorno é um objeto que representa os dados contidos na estrutura de siginfo_t, a saber: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band.

Disponibilidade: Unix.

Veja a página man sigwaitinfo(2) para mais informações.

Veja também pause(), sigwait() e sigtimedwait().

Adicionado na versão 3.3.

Alterado na versão 3.5: A função agora é tentada novamente se interrompida por um sinal que não esteja em sigset e o manipulador de sinal não levanta uma exceção (veja PEP 475 para a justificativa).

signal.sigtimedwait(sigset, timeout)

Semelhante a sigwaitinfo(), mas recebe um argumento timeout adicional que especifica um tempo limite. Se timeout for especificado como 0, uma consulta será realizada. Retorna None se ocorrer um tempo limite.

Disponibilidade: Unix.

Veja a página man sigtimedwait(2) para mais informações.

Veja também pause(), sigwait() e sigwaitinfo().

Adicionado na versão 3.3.

Alterado na versão 3.5: A função agora é tentada novamente com o timeout recalculado se interrompida por um sinal que não esteja em sigset e o manipulador de sinal não levanta uma exceção (veja PEP 475 para a justificativa).

Exemplos

Aqui está um programa de exemplo mínimo. Ele usa a função alarm() para limitar o tempo gasto esperando para abrir um arquivo; isso é útil se o arquivo for para um dispositivo serial que pode não estar ligado, o que normalmente faria com que o os.open() travasse indefinidamente. A solução é definir um alarme de 5 segundos antes de abrir o arquivo; se a operação demorar muito, o sinal de alarme será enviado e o manipulador levantará uma exceção.

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!")

# Define o manipulador de sinal e um alarme de 5 segundos
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# Este open() pode travar indefinidamente
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Desabilita o alarme

Nota sobre SIGPIPE

Canalizar a saída do seu programa para ferramentas como head(1) fará com que um sinal SIGPIPE seja enviado ao seu processo quando o receptor da saída padrão fechar antes do tempo. Isso resulta em uma exceção como BrokenPipeError: [Errno 32] Broken pipe. Para lidar com esse caso, envolva seu ponto de entrada para capturar essa exceção da seguinte maneira:

import os
import sys

def main():
    try:
        # simula uma saída grande (seu código substitui este loop)
        for x in range(10000):
            print("y")
        # limpa a saída aqui para forçar o acionamento do SIGPIPE
        # enquanto estiver dentro deste bloco try.
        sys.stdout.flush()
    except BrokenPipeError:
        # Python libera fluxos padrão na saída; redireciona a saída restante
        # para devnull para evitar outro BrokenPipeError no desligamento
        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()

Não defina a disposição de SIGPIPE como SIG_DFL para evitar BrokenPipeError. Isso faria com que seu programa encerrasse inesperadamente sempre que qualquer conexão de soquete fosse interrompida enquanto o programa ainda estivesse escrevendo nele.

Nota sobre manipuladores de sinais e exceções

Se um manipulador de sinais levantar uma exceção, a exceção será propagada para a thread principal e poderá ser levantada após qualquer instrução bytecode. Mais notavelmente, uma KeyboardInterrupt pode aparecer a qualquer momento durante a execução. A maioria dos códigos Python, incluindo a biblioteca padrão, não pode ser robusta contra isso, e, portanto, uma KeyboardInterrupt (ou qualquer outra exceção resultante de um manipulador de sinais) pode, em raras ocasiões, colocar o programa em um estado inesperado.

Para ilustrar esse problema, considere o seguinte código:

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

    def __enter__(self):
        # Se ocorrer KeyboardInterrupt aqui, está tudo bem
        self.lock.acquire()
        # Se KeyboardInterrupt ocorrer aqui, __exit__ não será chamado
        ...
        # KeyboardInterrupt pode ocorrer logo antes do retorno da função

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

Para muitos programas, especialmente aqueles que simplesmente desejam encerrar em KeyboardInterrupt, isso não é um problema, mas aplicações complexos ou que exigem alta confiabilidade devem evitar levantar exceções de manipuladores de sinal. Eles também devem evitar capturar KeyboardInterrupt como forma de encerrar o programa sem problemas. Em vez disso, devem instalar seu próprio manipulador SIGINT. Abaixo está um exemplo de um servidor HTTP que evita 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...")