web-dev-qa-db-fra.com

Capture clavierinterrupt dans Python sans try-except

Existe-t-il un moyen dans Python) de capturer l'événement KeyboardInterrupt sans placer tout le code dans une instruction try-except?

Je veux quitter proprement sans laisser de trace si l'utilisateur appuie sur Ctrl+C.

98
Alex

Oui, vous pouvez installer un gestionnaire d'interruptions à l'aide du module signal et attendre indéfiniment à l'aide de threading.Event :

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()
143
Johan Kotlinski

Si tout ce que vous voulez est de ne pas afficher la trace, faites votre code comme ceci:

## all your app logic here
def main():
   ## whatever your app does.


if __== "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(Oui, je sais que cela ne répond pas directement à la question, mais on ne comprend pas très bien pourquoi le blocage try/except est désagréable - cela le rend peut-être moins gênant pour l'OP)

34
bgporter

Une alternative à la définition de votre propre gestionnaire de signal consiste à utiliser un gestionnaire de contexte pour intercepter l'exception et l'ignorer:

>>> class CleanExit(object):
...     def __enter__(self):
...             return self
...     def __exit__(self, exc_type, exc_value, exc_tb):
...             if exc_type is KeyboardInterrupt:
...                     return True
...             return exc_type is None
... 
>>> with CleanExit():
...     input()    #just to test it
... 
>>>

Cela supprime le bloc try-except tout en préservant une mention explicite de ce qui se passe.

Cela vous permet également d'ignorer l'interruption uniquement dans certaines parties de votre code sans avoir à définir et à réinitialiser à nouveau les gestionnaires de signaux à chaque fois.

28
Bakuriu

Je sais que c’est une vieille question, mais j’y suis arrivé en premier, puis j’ai découvert le module atexit. Je ne connais pas encore ses antécédents multiplates-formes ni la liste complète des mises en garde, mais jusqu'à présent, c'est exactement ce que je cherchais pour tenter de gérer le nettoyage post -KeyboardInterrupt sous Linux. Je voulais juste jeter une autre façon d'aborder le problème.

Je souhaite effectuer un nettoyage post-sortie dans le contexte des opérations Fabric, aussi, tout encapsuler dans try/except n'était pas une option pour moi non plus. Je pense que atexit peut être un bon choix dans une telle situation, où votre code n'est pas au plus haut niveau du flux de contrôle.

atexit est très performant et lisible, par exemple:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

Vous pouvez également l'utiliser comme décorateur (à partir de la version 2.6; cet exemple provient de la documentation):

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

Si vous voulez le rendre spécifique à KeyboardInterrupt uniquement, la réponse d'une autre personne à cette question est probablement meilleure.

Notez cependant que le module atexit ne contient que ~ 70 lignes de code et qu'il ne serait pas difficile de créer une version similaire qui traite les exceptions différemment, par exemple en transmettant les exceptions en tant qu'arguments aux fonctions de rappel. (La limitation de atexit qui justifierait une version modifiée: actuellement, je ne peux concevoir un moyen de connaître les exceptions pour les fonctions exit-callback; le gestionnaire atexit intercepte l'exception , appelle votre/vos rappel (s), puis relance cette exception, mais vous pouvez le faire différemment.)

Pour plus d'informations, voir:

6
hangtwenty

Vous pouvez empêcher l’impression d’une trace de pile pour KeyboardInterrupt, sans try: ... except KeyboardInterrupt: pass _ (la solution la plus évidente et probablement la "meilleure", mais vous la connaissez déjà et avez demandé autre chose) en remplaçant sys.excepthook . Quelque chose comme

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)
4
user395760