callback interrupt tramite KeyboardInterrupt - twisted
-
29-09-2019 - |
Domanda
Al momento sto ripetendo un'operazione in un ciclo for all'interno di un callback utilizzando Contorto, ma vorrei il reattore per rompere il ciclo nel callback (uno) se i problemi degli utenti un KeyboardInterrupt tramite Ctrl-C. Da quanto ho provato, il reattore ferma solo o processi interrupt alla fine della richiamata.
C'è un modo di inviare un KeyboardInterrupt alla richiamata o il gestore di errori nel bel mezzo della corsa richiamata?
Saluti,
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()
Soluzione
Questa è voluto evitare di prelazione (semi), dal momento che ritorto è un sistema multitasking cooperativo. Ctrl-C è gestita in Python con un gestore SIGINT installato dall'interprete all'avvio. Il gestore imposta un flag quando viene richiamato. Dopo l'esecuzione di ogni codice di byte, l'interprete controlla la bandiera. Se è impostato, KeyboardInterrupt è sollevata in quel punto.
Il reattore viene installato il proprio gestore SIGINT. Questo sostituisce il comportamento del gestore dell'interprete. iniziati gestore del reattore reattore arresto. Dal momento che non solleva un'eccezione, non interrompe qualsiasi codice è in esecuzione. Il loop (o altro) ottiene alla fine, e quando il controllo viene restituito al reattore, procede arresto.
Se si preferisce avere Ctrl-C (cioè SIGINT) rilancio KeyboardInterrupt, allora si può solo ripristinare gestore SIGINT di Python utilizzando il modulo di segnalazione:
signal.signal(signal.SIGINT, signal.default_int_handler)
Si noti, tuttavia, che se si invia un SIGINT mentre il codice da ritorto è in esecuzione, piuttosto che il codice dell'applicazione stessa, non è definito il comportamento, come ritorto non si aspetta di essere interrotto da KeyboardInterrupt.
Altri suggerimenti
ho ottenuto questo damerino di lavoro. Il sparato SIGINT imposta un flag in esecuzione per qualsiasi attività in esecuzione nel mio codice, e inoltre le chiamate reactor.callFromThread (reactor.stop) per fermare qualsiasi codice in esecuzione 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()