2010-11-08 5 views
3

Я в настоящее время повторяю задачу в цикле for внутри обратного вызова с использованием Twisted, но хотел бы, чтобы реактор прерывал цикл в обратном вызове (один), если пользователь выдает KeyboardInterrupt через Ctrl- C. Из того, что я тестировал, реактор останавливается или обрабатывает прерывания в конце обратного вызова.обратный вызов с перекручиванием - прерывание через KeyboardInterrupt

Есть ли способ отправки KeyboardInterrupt для обратного вызова или обработчика ошибок в середине прогона обратного вызова?

Приветствия,

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() 

ответ

6

Это намеренно, чтобы избежать (полу) упреждение, так как Twisted является кооперативная система многозадачности. Ctrl-C обрабатывается в Python с помощью обработчика SIGINT, установленного интерпретатором при запуске. Обработчик устанавливает флаг при его вызове. После выполнения каждого байтового кода интерпретатор проверяет флаг. Если он установлен, KeyboardInterrupt поднимается в этой точке.

Реактор устанавливает собственный обработчик SIGINT. Это заменяет поведение обработчика интерпретатора. Обработчик реактора инициирует остановку реактора. Поскольку он не вызывает исключения, он не прерывает работу любого кода. Петля (или что-то другое) доходит до конца, и когда управление возвращается в реактор, происходит останов.

Если вы не хотите иметь Ctrl-C (т.е. SIGINT) поднять KeyboardInterrupt, то вы можете просто восстановить обработчик SIGINT Python, с помощью модуля сигнала:

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

Заметим, однако, что если вы отправляете SIGINT в то время как код из Twisted работает, а не ваш собственный код приложения, поведение не определено, поскольку Twisted не ожидает прерывания KeyboardInterrupt.

+0

Пол: Большое спасибо за объяснение! Я спрашивал себя, почему в половине случаев, если я прервал сигнал, код успешно завершился с моего кода, но вызвал исключение, ожидая в цикле реактора. – user500869

8

Я получил этот рабочий денди. Обожженный SIGINT устанавливает флаг работает для любой выполняемой задачи в моем коде, и дополнительно вызывает reactor.callFromThread (reactor.stop), чтобы остановить любой витую бега код:

#!/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()