web-dev-qa-db-fra.com

Capture de KeyboardInterrupt dans Python pendant l'arrêt du programme

J'écris un utilitaire de ligne de commande dans Python qui, puisqu'il s'agit d'un code de production, devrait pouvoir se fermer proprement sans décharger un tas de choses (codes d'erreur, traces de pile, etc.). ) à l’écran. Cela signifie que j’ai besoin d’attraper les interruptions du clavier.

J'ai essayé d'utiliser à la fois un bloc catch try comme:

if __== '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print 'Interrupted'
        sys.exit(0)

et capturer le signal lui-même (comme dans this post ):

import signal
import sys

def sigint_handler(signal, frame):
    print 'Interrupted'
    sys.exit(0)
signal.signal(signal.SIGINT, sigint_handler)

Les deux méthodes semblent bien fonctionner en fonctionnement normal. Cependant, si l'interruption survient pendant le code de nettoyage à la fin de l'application, Python semble toujours imprimer quelque chose à l'écran. L'interception de l'interruption donne

^CInterrupted
Exception KeyboardInterrupt in <bound method MyClass.__del__ of <path.to.MyClass object at 0x802852b90>> ignored

alors que le traitement du signal donne soit

^CInterrupted
Exception SystemExit: 0 in <Finalize object, dead> ignored

ou

^CInterrupted
Exception SystemExit: 0 in <bound method MyClass.__del__ of <path.to.MyClass object at 0x802854a90>> ignored

Non seulement ces erreurs sont laides, mais elles ne sont pas très utiles (surtout pour un utilisateur final sans code source)!

Le code de nettoyage de cette application est assez volumineux, il y a donc de bonnes chances que ce problème soit touché par de vrais utilisateurs. Y a-t-il un moyen de capturer ou de bloquer cette sortie, ou est-ce juste quelque chose que je devrai traiter?

50
Dan

Checkout this thread , il contient des informations utiles sur la sortie et la trace.

Si vous êtes plus intéressé par la suppression du programme, essayez quelque chose comme ceci (cela enlèvera également les bases du code de nettoyage):

if __== '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print('Interrupted')
        try:
            sys.exit(0)
        except SystemExit:
            os._exit(0)
81
Dan Hogan

Vous pouvez ignorer les SIGINT après le début de l'arrêt en appelant signal.signal(signal.SIGINT, signal.SIG_IGN) avant de commencer votre code de nettoyage.

5
Dan Getz