2010-11-08 27 views
3

Je répète actuellement une tâche dans une boucle à l'intérieur d'un rappel à l'aide Twisted, mais je voudrais le réacteur pour briser la boucle dans le rappel (un) si l'utilisateur émet un KeyboardInterrupt via Ctrl- C. D'après ce que j'ai testé, le réacteur ne s'arrête ou ne traite que les interruptions à la fin du rappel.torsadé - rappel via interruption KeyboardInterrupt

Est-il possible d'envoyer un KeyboardInterrupt au rappel ou le gestionnaire d'erreurs au milieu de la course de rappel?

Cheers,

Chris

#!/usr/bin/env python 

from twisted.internet import reactor, defer 


def one(result): 
    print "Start one()" 
    for i in xrange(10000): 
     print i 
    print "End one()" 
    reactor.stop() 


def oneErrorHandler(failure): 
    print failure 
    print "INTERRUPTING one()" 
    reactor.stop()  


if __name__ == '__main__': 

    d = defer.Deferred() 
    d.addCallback(one) 
    d.addErrback(oneErrorHandler) 
    reactor.callLater(1, d.callback, 'result') 

    print "STARTING REACTOR..." 
    try: 
     reactor.run() 
    except KeyboardInterrupt: 
     print "Interrupted by keyboard. Exiting." 
     reactor.stop() 

Répondre

6

Ceci est intentionnel pour éviter (semi-) préemption, car Twisted est un système multi-tâches coopératif. Ctrl-C est géré en Python avec un gestionnaire SIGINT installé par l'interpréteur au démarrage. Le gestionnaire définit un indicateur lorsqu'il est appelé. Après l'exécution de chaque code d'octet, l'interpréteur vérifie l'indicateur. Si elle est définie, KeyboardInterrupt est déclenché à ce stade.

Le réacteur installe son propre gestionnaire de SIGINT. Cela remplace le comportement du gestionnaire de l'interpréteur. Le manipulateur du réacteur déclenche l'arrêt du réacteur. Comme il ne déclenche pas d'exception, il n'interrompt pas le code en cours d'exécution. La boucle (ou quoi que ce soit) finit, et lorsque le contrôle est renvoyé au réacteur, l'arrêt se poursuit.

Si vous préférez avoir Ctrl-C (c.-à-SIGINT) augmenter KeyboardInterrupt, vous pouvez simplement restaurer le gestionnaire de SIGINT Python à l'aide du module de signal: cependant,

signal.signal(signal.SIGINT, signal.default_int_handler) 

Notez que si vous envoyez un SIGINT alors que le code de Twisted est en cours d'exécution, plutôt que votre propre code d'application, le comportement n'est pas défini car Twisted ne s'attend pas à être interrompu par KeyboardInterrupt.

+0

Paul: Merci beaucoup pour la explication! Je me demandais pourquoi la moitié du temps, si je l'interrompais avec un signal, le code s'est rompu avec succès de mon code mais a produit une exception en attendant dans la boucle du réacteur. – user500869

8

J'ai obtenu ce dandy de travail. Le SIGINT tiré établit un drapeau en cours d'exécution pour toute tâche en cours d'exécution dans mon code, et appelle en outre reactor.callFromThread (reactor.stop) arrêter tout code de fonctionnement twisted:

#!/usr/bin/env python 

import sys 
import twisted 
import re 
from twisted.internet import reactor, defer, task 
import signal 


def one(result, token): 
    print "Start one()" 
    for i in xrange(1000): 
     print i 
     if token.running is False: 
      raise KeyboardInterrupt() 
      #reactor.callFromThread(reactor.stop) # this doesn't work 
    print "End one()" 

def oneErrorHandler(failure): 
    print "INTERRUPTING one(): Unkown Exception" 
    import traceback 
    print traceback.format_exc() 
    reactor.stop() 

def oneKeyboardInterruptHandler(failure): 
    failure.trap(KeyboardInterrupt) 
    print "INTERRUPTING one(): KeyboardInterrupt" 
    reactor.stop() 

def repeatingTask(token): 
    d = defer.Deferred() 
    d.addCallback(one, token) 
    d.addErrback(oneKeyboardInterruptHandler) 
    d.addErrback(oneErrorHandler) 
    d.callback('result') 

class Token(object): 
    def __init__(self): 
     self.running = True 

def sayBye(): 
    print "bye bye." 


if __name__ == '__main__': 

    token = Token() 

    def customHandler(signum, stackframe): 
     print "Got signal: %s" % signum 
     token.running = False    # to stop my code 
     reactor.callFromThread(reactor.stop) # to stop twisted code when in the reactor loop 
    signal.signal(signal.SIGINT, customHandler) 

    t2 = task.LoopingCall(reactor.callLater, 0, repeatingTask, token) 
    t2.start(5) 

    reactor.addSystemEventTrigger('during', 'shutdown', sayBye) 

    print "STARTING REACTOR..." 
    reactor.run()