dan*_*ast 7 python twisted interrupt keyboard-events keyboardinterrupt
所以我要做的是:
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
Run Code Online (Sandbox Code Playgroud)
问题是,虽然菜单被通知它必须停止,并且很快就会这样做,但它现在无法做到,因为它被困在raw_input:
def askUser(self):
current_date = datetime.now().isoformat(' ')
choice = raw_input('%s > ' % current_date)
return choice
Run Code Online (Sandbox Code Playgroud)
因此,由于twisted正在删除默认的中断处理程序,raw_input因此不会停止.我仍然需要按enter后^C它停止.
如何强制raw_input停止,而不安装默认的中断处理程序,这是扭曲上下文中的问题来源(因为扭曲本身不会被期望中断)
我猜这个问题与之无关raw_input:任何采取无限时间(或超过预设限制)的函数都应该以某种方式中断.
这有可接受的扭曲模式吗?
这是完整的测试代码:
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()
Run Code Online (Sandbox Code Playgroud)
我可以用两种不同的方式运行.
正常方式:
menu = Menu()
menu.forever()
Run Code Online (Sandbox Code Playgroud)
按下可^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
Run Code Online (Sandbox Code Playgroud)
正如所料.
扭曲的方式:
menu = Menu()
menutw = MenuTwisted(menu)
menutw.run()
Run Code Online (Sandbox Code Playgroud)
按下^C将产生:
A) Do A
B) Do B
2013-12-03 11:04:18.678219 > Please select option > ^CInterrupted!
Run Code Online (Sandbox Code Playgroud)
但askUser实际上没有打断:我还需要按enter的raw_input完成.
处理这个问题的正确方法是异步处理控制台输入,而不是尝试使阻塞输入函数可中断。换句话说,raw_input对于您正在攻击的问题来说,这从根本上来说是错误的解决方案。
然而,如果你真的只是想了解这里发生了什么,诀窍是在调用 后reactor.callFromThread(reactor.stop),你必须以某种方式提示raw_input退出;正常情况下是不会的。但是,由于您在线程中运行它,因此它实际上根本不可中断,因为在 Python 中只有主线程是可中断的。所以我认为你想要的实际上可能是不可能的。我相信,也许关闭sys.stdin可能会从下面拉出地毯raw_input,即使它在一个线程中,但似乎底层库正在做一些比简单地从 FD 读取更聪明的事情,所以关闭它并没有什么好处。