Interrupt raw_input in a twisted program

I will talk about this explanation and a workaround:

So what I do:

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 

The problem is that although the menu is notified that it should stop and will do it soon, it will not be able to do it now, as it is stuck in raw_input :

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

So, since twisted removes the default interrupt handler, raw_input does not stop. I still need to press enter after ^C to stop it.

How can I make raw_input be stopped without setting a default interrupt handler, which is the source of problems in a twisted context (since the twisted itself does not expect an interrupt)

I think that the problem is not only related to raw_input : any function that executes for an unlimited time (or longer than the set limit) must somehow be interrupted.

Is there a valid twisted pattern for this?

EDIT

This is the full test code:

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

What I can run in two different ways.

The usual way:

 menu = Menu() menu.forever() 

Pressing ^C immediately stops the program:

 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 

As expected.

Twisted path:

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

Pressing ^C will result in:

 A) Do A B) Do B 2013-12-03 11:04:18.678219 > Please select option > ^CInterrupted! 

But askUser is not really interrupted: I still need to press enter for raw_input to complete.

+6
source share
1 answer

The right way to handle this is to control console input asynchronously , instead of trying to block the input lock function. In other words, raw_input is a fundamentally wrong solution to the problem you are attacking.

However, if you really want to understand what is happening here, the trick is that after calling reactor.callFromThread(reactor.stop) you need to somehow call raw_input to exit; it is not normal. However, since you run it on a thread, it does not interrupt at all, because in Python only the main thread is interrupted . Therefore, I think that what you want may actually be impossible. I figured that maybe closing sys.stdin could get the carpet out of under raw_input , even if it was in the stream, but it seems that the underlying libraries do something smarter than just reading from FD, so closing it does not do well .

+2
source

Source: https://habr.com/ru/post/959189/


All Articles