在扭曲程序中如何打断raw_input?

7
我将参考这个解释和这个解决方法
所以我正在做的是:
def interrupted(signum, stackframe):
    log.warning('interrupted > Got signal: %s', signum)
    menu.quitMenu = True # to stop my code

signal.signal(signal.SIGINT, interrupted) # Handle KeyboardInterrupt

问题在于,虽然菜单已经被通知需要停止,并且很快会停止,但是它现在无法停止,因为它被卡在了一个raw_input中:

def askUser(self):
    current_date   = datetime.now().isoformat(' ')
    choice = raw_input('%s > ' % current_date)
    return choice

因为Twisted正在删除默认的中断处理程序,所以 raw_input 没有停止。我仍然需要在 ^C 后按下 enter 才能让它停止。

我如何强制停止 raw_input而不必安装默认的中断处理程序?在Twisted的上下文中,安装默认的中断处理程序会带来问题(因为Twisted本身没有预计到会被中断)

我想问题不仅仅和 raw_input 有关:任何需要花费不可限定时间(或超过预设限制)的函数都应该被中断。

是否有一个被接受的Twisted模式可以解决这个问题?

编辑

这是完整的测试代码:

from datetime import datetime

class Menu:

    def __init__(self):
        self.quitMenu = False

    def showMenu(self):
        print '''

A) Do A
B) Do B

'''

    def askUser(self):
        current_date   = datetime.now().isoformat(' ')
        choice = raw_input('%s > Please select option > ' % current_date)
        print
        return choice

    def stopMe(self):
        self.quitMenu = True

    def alive(self):
        return self.quitMenu == False

    def doMenuOnce(self):
        self.showMenu()
        choice = self.askUser()
        if not self.alive() : # Maybe somebody has tried to stop the menu while in askUser
            return
        if   choice == 'A' : print 'A selected'
        elif choice == 'B' : print 'B selected'
        else               : print 'ERR: choice %s not supported' % (choice)

    def forever(self):
        while self.alive():
            self.doMenuOnce()

from twisted.internet import reactor, threads
import signal

class MenuTwisted:

    def __init__(self, menu):
        self.menu = menu
        signal.signal(signal.SIGINT, self.interrupted) # Handle KeyboardInterrupt

    def interrupted(self, signum, stackframe):
        print 'Interrupted!'
        self.menu.stopMe()

    def doMenuOnce(self):
        threads.deferToThread(self.menu.doMenuOnce).addCallback(self.forever)

    def forever(self, res=None):
        if self.menu.alive() :
            reactor.callLater(0, self.doMenuOnce)
        else : 
            reactor.callFromThread(reactor.stop)

    def run(self):
        self.forever()
        reactor.run()

我可以以两种不同的方式运行。
普通方式:
menu = Menu()
menu.forever()

按下 ^C 会立即停止程序:

A) Do A
B) Do B


2013-12-03 11:00:26.288846 > Please select option > ^CTraceback (most recent call last):
  File "twisted_keyboard_interrupt.py", line 72, in <module>
    menu.forever()
  File "twisted_keyboard_interrupt.py", line 43, in forever
    self.doMenuOnce()
  File "twisted_keyboard_interrupt.py", line 34, in doMenuOnce
    choice = self.askUser()
  File "twisted_keyboard_interrupt.py", line 22, in askUser
    choice = raw_input('%s > Please select option > ' % current_date)
KeyboardInterrupt

如预期所料。

扭曲的方式:

menu = Menu()
menutw = MenuTwisted(menu)
menutw.run()

按下^C会产生以下结果:
A) Do A
B) Do B


2013-12-03 11:04:18.678219 > Please select option > ^CInterrupted!

但是askUser实际上没有被打断: 我仍然需要按下enter键才能完成raw_input的操作。

1个回答

3
处理这个问题的正确方法是以异步方式处理控制台输入, 而不是尝试使阻塞输入函数可中断。换句话说,raw_input基本上是你正在解决的问题的错误解决方案。
然而,如果你真的只想了解这里发生了什么,诀窍在于在调用reactor.callFromThread(reactor.stop)之后,你必须以某种方式提示raw_input退出;它通常不会退出。但是,由于你正在一个线程中运行它,它实际上是不可中断的,因为只有Python的主线程是可中断的。所以我认为你想要的可能实际上是不可能的。我认为,也许关闭sys.stdin可以使raw_input失效,即使它在一个线程中,但似乎底层库正在做比简单地从FD读取更聪明的事情,所以关闭它没有任何作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接