web-dev-qa-db-fra.com

Comment capturer SIGINT en Python?

Je travaille sur un script python qui démarre plusieurs processus et connexions à une base de données. De temps en temps, je veux tuer le script avec un Ctrl+C signal, et je voudrais faire un peu de nettoyage.

En Perl, je ferais ceci:

$SIG{'INT'} = 'exit_gracefully';

sub exit_gracefully {
    print "Caught ^C \n";
    exit (0);
}

Comment est-ce que je fais l'analogue de ceci en Python?

477
James Thompson

Enregistrez votre gestionnaire avec signal.signal comme ceci:

#!/usr/bin/env python
import signal
import sys
def signal_handler(sig, frame):
        print('You pressed Ctrl+C!')
        sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()

Code adapté de ici .

Plus de documentation sur signal peut être trouvée ici .

667
Matt J

Vous pouvez le traiter comme une exception (KeyboardInterrupt), comme un autre. Créez un nouveau fichier et lancez-le à partir de votre shell avec le contenu suivant pour voir ce que je veux dire:

import time, sys

x = 1
while True:
    try:
        print x
        time.sleep(.3)
        x += 1
    except KeyboardInterrupt:
        print "Bye"
        sys.exit()
147
rledley

Et en tant que gestionnaire de contexte:

import signal

class GracefulInterruptHandler(object):

    def __init__(self, sig=signal.SIGINT):
        self.sig = sig

    def __enter__(self):

        self.interrupted = False
        self.released = False

        self.original_handler = signal.getsignal(self.sig)

        def handler(signum, frame):
            self.release()
            self.interrupted = True

        signal.signal(self.sig, handler)

        return self

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):

        if self.released:
            return False

        signal.signal(self.sig, self.original_handler)

        self.released = True

        return True

Utiliser:

with GracefulInterruptHandler() as h:
    for i in xrange(1000):
        print "..."
        time.sleep(1)
        if h.interrupted:
            print "interrupted!"
            time.sleep(2)
            break

Gestionnaires imbriqués:

with GracefulInterruptHandler() as h1:
    while True:
        print "(1)..."
        time.sleep(1)
        with GracefulInterruptHandler() as h2:
            while True:
                print "\t(2)..."
                time.sleep(1)
                if h2.interrupted:
                    print "\t(2) interrupted!"
                    time.sleep(2)
                    break
        if h1.interrupted:
            print "(1) interrupted!"
            time.sleep(2)
            break

De là: https://Gist.github.com/2907502

57
Udi

Vous pouvez gérer CTRL+C en interceptant l'exception KeyboardInterrupt. Vous pouvez implémenter n'importe quel code de nettoyage dans le gestionnaire d'exceptions.

26
Jay Conrod

De Python's documentation :

import signal
import time

def handler(signum, frame):
    print 'Here you go'

signal.signal(signal.SIGINT, handler)

time.sleep(10) # Press Ctrl+c here
19
sunqiang

Encore un extrait

mainrenvoyé comme fonction principale et exit_gracefully comme CTRL + c gestionnaire

if __== '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        exit_gracefully()
13
Jossef Harush

J'ai adapté le code de @udi pour prendre en charge plusieurs signaux (rien d'extraordinaire):

class GracefulInterruptHandler(object):
    def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
        self.signals = signals
        self.original_handlers = {}

    def __enter__(self):
        self.interrupted = False
        self.released = False

        for sig in self.signals:
            self.original_handlers[sig] = signal.getsignal(sig)
            signal.signal(sig, self.handler)

        return self

    def handler(self, signum, frame):
        self.release()
        self.interrupted = True

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):
        if self.released:
            return False

        for sig in self.signals:
            signal.signal(sig, self.original_handlers[sig])

        self.released = True
        return True

Ce code prend en charge l'appel d'interruption au clavier (SIGINT) et la SIGTERM (kill <process>)

7
Cyril N.

Vous pouvez utiliser les fonctions du module signal intégré de Python pour configurer des gestionnaires de signaux en python. Plus précisément, la fonction signal.signal(signalnum, handler) est utilisée pour enregistrer la fonction handler pour le signal signalnum.

4
Brandon E Taylor

Contrairement à Matt J sa réponse, j’utilise un objet simple. Cela me donne la possibilité d’analyser ce gestionnaire avec tous les threads devant être arrêtés en toute sécurité.

class SIGINT_handler():
    def __init__(self):
        self.SIGINT = False

    def signal_handler(self, signal, frame):
        print('You pressed Ctrl+C!')
        self.SIGINT = True


handler = SIGINT_handler()
signal.signal(signal.SIGINT, handler.signal_handler)

Autre part

while True:
    # task
    if handler.SIGINT:
        break
3
Thomas Devoogdt

merci pour les réponses existantes, mais a ajouté signal.getsignal()

import signal

# store default handler of signal.SIGINT
default_handler = signal.getsignal(signal.SIGINT)
catch_count = 0

def handler(signum, frame):
    global default_handler, catch_count
    catch_count += 1
    print ('wait:', catch_count)
    if catch_count > 3:
        # recover handler for signal.SIGINT
        signal.signal(signal.SIGINT, default_handler)
        print('expecting KeyboardInterrupt')

signal.signal(signal.SIGINT, handler)
print('Press Ctrl+c here')

while True:
    pass
0
gsw945

Personnellement, je ne pouvais pas utiliser try/except KeyboardInterrupt car j'utilisais le mode de socket standard (IPC) qui bloquait. Le SIGINT a donc été créé, mais il n’a été reçu qu’après avoir reçu des données sur le socket.

Définir un gestionnaire de signal se comporte de la même manière.

Par contre, cela ne fonctionne que pour un terminal réel. D'autres environnements de départ pourraient ne pas accepter Ctrl + C ou pré-manipuler le signal.

De plus, il y a des "Exceptions" et des "BaseExceptions" dans Python, qui se différencient par le fait que l'interpréteur doit quitter lui-même proprement, de sorte que certaines exceptions ont une priorité plus élevée que d'autres (Exceptions est dérivée de BaseException)

0
Agnes K. Cathex