J'ai un script qui fonctionne sur une boucle infinie et ajoute des éléments à une base de données et effectue des tâches que je ne peux pas arrêter juste au milieu, donc je ne peux pas appuyer simplement sur Ctrl + C et l'arrêter.
Je veux pouvoir en quelque sorte arrêter une boucle while, mais la laisser terminer sa dernière itération avant qu'elle ne s'arrête.
Permettez-moi de préciser:
mon code ressemble à ceci:
while True:
does something
does more things
does more things
Je veux pouvoir interrompre la boucle while à la fin ou au début, mais pas entre les choses parce que ce serait mauvais.
et je ne veux pas qu'il me demande après chaque itération si je veux continuer.
merci pour les bonnes réponses, je suis très reconnaissant, mais mon implémentation ne semble pas fonctionner:
def signal_handler(signal, frame):
global interrupted
interrupted = True
class Crawler():
def __init__(self):
# not relevent
def crawl(self):
interrupted = False
signal.signal(signal.SIGINT, signal_handler)
while True:
doing things
more things
if interrupted:
print("Exiting..")
break
quand je presse ctr + c le programme ne cesse de m'ignorer
Ce que vous devez faire, c'est intercepter l'interruption, définir un drapeau indiquant que vous avez été interrompu, puis continuer à travailler jusqu'à ce qu'il soit temps de vérifier le drapeau (à la fin de chaque boucle). Étant donné que la construction try-except de python abandonnera l'exécution actuelle de la boucle, vous devez configurer un gestionnaire de signal approprié; il gérera l’interruption mais laissera python continuer là où il s’est arrêté. Voici comment:
import signal
import time # For the demo only
def signal_handler(signal, frame):
global interrupted
interrupted = True
signal.signal(signal.SIGINT, signal_handler)
interrupted = False
while True:
print("Working hard...")
time.sleep(3)
print("All done!")
if interrupted:
print("Gotta go")
break
Remarques:
Utilisez ceci à partir de la ligne de commande. Dans la console IDLE, le traitement des interruptions par IDLE sera piétiné.
Une meilleure solution consisterait à "bloquer" KeyboardInterrupt pendant la durée de la boucle et à la débloquer au moment d'interroger les interruptions. Ceci est une caractéristique de certains types Unix mais pas de tous, d’où python ne le supporte pas (voir la troisième "règle générale")
Le PO veut faire cela à l'intérieur d'une classe. Mais la fonction d'interruption est appelée par le système de traitement du signal, avec deux arguments: le numéro du signal et un pointeur sur le cadre de la pile - aucune place pour un argument self
donnant accès à l'objet de classe. Par conséquent, le moyen le plus simple de définir un indicateur consiste à utiliser une variable globale. Vous pouvez configurer un pointeur sur le contexte local à l’aide de fermetures (c’est-à-dire définir le gestionnaire de signaux de manière dynamique dans __init__()
, mais franchement, cela ne me dérangerait pas que si un global est hors de question en raison du multi-threading ou autre.
Caveat: Si votre processus est en cours d'appel système, le traitement d'un signal peut interrompre l'appel système. Donc, cela peut ne pas être sûr pour toutes les applications. Des alternatives plus sûres seraient: (a) au lieu de compter sur des signaux, utilisez une lecture non bloquante à la fin de chaque itération de boucle (et tapez entrée au lieu de frapper ^ C); (b) utiliser des threads ou une communication interprocessus pour isoler le travailleur de la gestion du signal; ou (c) effectuez le travail de mise en œuvre du blocage réel du signal , si vous êtes sur un système d'exploitation qui en est équipé. Tous dépendent de l'OS dans une certaine mesure, alors je vais en rester là.
la logique ci-dessous vous aidera à le faire,
import signal
import sys
import time
run = True
def signal_handler(signal, frame):
global run
print "exiting"
run = False
signal.signal(signal.SIGINT, signal_handler)
while run:
print "hi"
time.sleep(1)
# do anything
print "bye"
tout en exécutant ceci, essayez d'appuyer sur CTRL + C
Pour clarifier la solution de @ praba230890: La variable interrupted
n'a pas été définie dans la portée correcte. Il a été défini dans la fonction crawl
et le gestionnaire ne peut pas l'atteindre en tant que variable globale, selon la définition du gestionnaire à la racine du programme.
J'espère que le code ci-dessous vous aiderait:
#!/bin/python
import sys
import time
import signal
def cb_sigint_handler(signum, stack):
global is_interrupted
print "SIGINT received"
is_interrupted = True
if __== "__main__":
is_interrupted = False
signal.signal(signal.SIGINT, cb_sigint_handler)
while(1):
# do stuff here
print "processing..."
time.sleep(3)
if is_interrupted:
print "Exiting.."
# do clean up
sys.exit(0)