Прерывание raw_input в витой программе
Я буду ссылаться на это объяснение и этот обходной путь:
Итак, что я делаю, это:
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
не остановлен Мне еще нужно нажать 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()
Который я могу запустить двумя разными способами.
Нормальный способ:
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 ответ
Правильный способ справиться с этим - обрабатывать ввод с консоли асинхронно, а не пытаться сделать блокирующую функцию ввода прерываемой. Другими словами, raw_input
это принципиально неправильное решение проблемы, которую вы атакуете.
Однако, если вы действительно хотите понять, что здесь происходит, дело в том, что после вызова reactor.callFromThread(reactor.stop)
надо как-то подсказать raw_input
выходить; это не будет нормально. Тем не менее, поскольку вы запускаете его в потоке, на самом деле он вообще не прерывается, потому что в Python прерывается только основной поток. Поэтому я думаю, что вы хотите, может быть на самом деле невозможно. Я считал что возможно закрытие sys.stdin
может вытащить коврик из-под raw_input
даже если бы это было в потоке, но кажется, что базовые библиотеки делают что-то более умное, чем простое чтение из FD, поэтому закрытие не приносит пользы.