23

I've been writing a small utility application using Python 3 (the below testcase also works in Python 2, however) and PyQt 4 that uses the code module to spawn a REPL prompt allowing interaction with a Qt window.

Unfortunately I've hit a problem I've been unable to solve: When I exit() the app while code is inside input() (known as raw_input() in Python 2.x), my Linux terminal subsequently no longer echoes typed characters. I.e. the terminal appears to be left in a broken state, presumably due to some escape sequence issued by input().

I've tried a variety of approaches to fix this, from using the curses module and other means to reset the terminal prior to running exit, to trying to emulate the stdin stream to exit by actually handing exit() to input() (unfornunately code.InteractiveConsole.push() does not work that way, as one might think it would), to trying to write my own non-blocking input() using threading, but I've been unable to pull together something working.

Here, here, here and here are discussions of similar problems.

Finally, here is a reduced testcase to demonstrate the problem:

#!/usr/bin/env python3

import code
import sys
from PyQt4.QtGui import QApplication, QWidget

app = QApplication(sys.argv)

app.lastWindowClosed.connect(exit)

widget = QWidget()
widget.show()

code.interact()

For those unfamiliar with (Py)Qt, this will open a blank window, and when it is closed, the connection from app's lastWindowClosed signal will cause a call to the built-in exit() function to happen. This occurs while the code module is executing a call to input() to read from sys.stdin. And here, when I close the window, typing into the terminal afterwards doesn't show any of the types characters.

I'm mainly using Python 3, and the actual app uses Python 3-specific code, but I've tried the testcase in Python 2.7 as well and it shows the same problem.

Community
  • 1
  • 1
Sho
  • 231
  • 2
  • 4
  • I can not reproduce the problem in win7 64bit python26. If I close the qt frame the console brings again the normal prompt and I can enter text on it. – joaquin Oct 29 '11 at 11:05
  • 2
    I should have mentioned that I'm running this on Linux (I'll edit to mention that). The terminal behavior probably differs substantially between the platforms. Thanks for checking, though! – Sho Oct 29 '11 at 11:08

4 Answers4

33

Try os.system('stty sane'). The stty sane is supposed to reset echo, and some other things apparently.

Quentin Engles
  • 2,744
  • 1
  • 20
  • 33
  • 1
    Brilliant! The `stty sane` command is indeed getting the terminal out of *raw* mode without clearing the terminal itself which is what happens when using the `reset` command. Thanks! – Tim Visée Jun 24 '15 at 10:49
  • This answer should be accepted. Indeed solves the problem. – udondan Jan 25 '16 at 05:19
  • 1
    Can confirm this works. For those who are new: You have to do this in the Python interpreter and `import os` first ;) One-liner to paste into a broken terminal is `import os; os.system('stty sane')` – sudo Jan 31 '17 at 00:13
  • is there a way to do this in native python without shelling out? – ThorSummoner Nov 16 '17 at 01:26
  • @ThorSummoner I don't think so. As far as I know python doesn't have much utilities for interacting with the terminal directly except for `raw_input()`. I don't even think it could do it without the core of python being modified just for that purpose. Maybe a module written in C could do it. Or an issue suggesting support for this could be submitted to the python repo. – Quentin Engles Nov 16 '17 at 08:35
  • 1
    +1 This also works for me when my MacOS terminal gets into a weird "noecho" state. I just type `stty sane` (though I can't see it) and everything is fine. – Bennett McElwee Nov 28 '17 at 21:00
21

This is no real solution to the problem, but if you type "reset" in the terminal after you've closed the app, it goes back to normal.

I had similar issues once when developing a c application that didn't close a pipe correctly. Maybe something similar is happening here aswell.

Jakob
  • 211
  • 1
  • 2
3

The answer from Quentin Engles worked for me too but as a Python neophyte I didn't understand where the stty sane was supposed to go. After some hunting and head scratching I figured out that exit was a reference to the exit() method so I created exiting() and passed a reference to it:

#!/usr/bin/env python3

import code
import sys
from PyQt4.QtGui import QApplication, QWidget

def exiting():
    os.system('stty sane')
    exit()

app = QApplication(sys.argv)

app.lastWindowClosed.connect(exiting)

widget = QWidget()
widget.show()

code.interact()
Community
  • 1
  • 1
Wink Saville
  • 1,020
  • 1
  • 7
  • 7
0

I've run in to the same problem using the curses module. Using the other answer on this page, I've sidestepped the problem with import os at the beginning of the program, and then ending the program with os.system('reset').

worc
  • 3,654
  • 3
  • 31
  • 35
  • With curses, look into the [curses.wrapper()](https://docs.python.org/2/library/curses.html#curses.wrapper) method. It sets up some of the most common curses settings and unsets them when your code ends, even if the program is killed by an exception. – Surreal Dreams Jul 13 '18 at 17:22