Витая - прерывание обратного вызова через KeyboardInterrupt

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

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

Ура,

Крис

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

2 ответа

Решение

Это сделано для того, чтобы избежать (полу) упреждения, поскольку 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.

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

#!/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()
Другие вопросы по тегам