# `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.

On WebAssembly platforms `wasm32-emscripten` and `wasm32-wasi`, signals are emulated and therefore behave differently. Several functions and signals are not available on these platforms.

### 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).

  Disponibilidade: Unix.

signal.SIGBREAK

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

  Disponibilidade: Windows.

signal.SIGBUS

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

  Disponibilidade: Unix.

signal.SIGCHLD

  Processo filho interrompido ou encerrado.

  Disponibilidade: Unix.

signal.SIGCLD

  Apelido para `SIGCHLD`.

  Disponibilidade: not macOS.

signal.SIGCONT

  Continua o processo se ele estiver parado no momento

  Disponibilidade: Unix.

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.

  Disponibilidade: Unix.

signal.SIGILL

  Instrução ilegal.

signal.SIGINT

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

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

signal.SIGKILL

  Sinal para encerrar forçadamente.

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

  Disponibilidade: Unix.

signal.SIGPIPE

  Encadeamento quebrado: escreve no encadeamento sem leitores.

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

  Disponibilidade: Unix.

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.

  Disponibilidade: Unix.

signal.SIGUSR2

  Sinal definido pelo usuário 2.

  Disponibilidade: Unix.

signal.SIGWINCH

  Sinal de redimensionamento do Windows.

  Disponibilidade: Unix.

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

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

  Disponibilidade: Unix.

signal.getitimer(which)

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

  Disponibilidade: Unix.

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

  Set the wakeup file descriptor to fd. When a signal is received, the signal number is written as a single byte into the fd. This can be used by a library to wakeup a poll or select call, allowing the signal to be fully processed.

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