Reputation: 3742
I am investigating a bug where curses.wrapper
does not restore the terminal properly. The issue is shown after a backgrounding/foregrounding sequence.
Consider the following python program saved in myprogram.py
:
import curses, subprocess
# Function that does nothing
def f(*args, **kwargs):
pass
curses.wrapper(f)
# Call vi to open a file
subprocess.call("vi /tmp/foo", shell=True)
Steps to repro the issue:
python myprogram.py
/tmp/foo
ctrl-z
it brings me back to my shellfg
Removing the curses.wrapper(f)
line makes the program works: the editor is drawn properly when the program is resumed.
I tried multiple things, like replacing the call to curses.wrapper(f)
by what it actually does and, the most minimal example (i.e. calling initscr
, endwin
) leads also to the same issue.
I am running:
What am I missing?
Upvotes: 3
Views: 1430
Reputation: 54563
The source-code for curses.wrapper does nothing special with signals.
During initialization (such as a call to initscr
), the ncurses library adds handlers for these signals: SIGINT
, SIGTERM
, SIGTSTP
, SIGWINCH
. For whatever reason (likely because it is an internal detail not directly visible to callers), this is documented mainly in the NEWS
file.
Applications which need to add their own signal handler should do this after ncurses' initialization (since ncurses does this only once). Because curses applications can be switched to/from screen-mode, the signal handlers are left active until the program quits. For instance, it would be possible for a Python script to call curses.wrapper
more than once (although it probably would not work correctly except with ncurses -- X/Open says that "portable applications must not
call initscr more than once").
Saving and restoring the signal handler state as suggested by @lc2817 will work—but it is a workaround because it is not elegant. If curses.wrapper
were modified to add some state to it, to remember if it was called before, and to save/restore the signal-handlers, the workaround would be unnecessary. To make it really portable, initscr
should be called on the first use, and refresh
on subsequent uses.
Upvotes: 3
Reputation: 3742
This happens to be a bug in curses.wrapper
or anything underneath that forgets to restore signal handler to their previous values.
This fixes it:
import curses, subprocess
import signal
# Function that does nothing
def f(*args, **kwargs):
pass
a = signal.getsignal(signal.SIGTSTP)
curses.wrapper(f)
signal.signal(signal.SIGTSTP, a)
# Call vi to open a file
subprocess.call("vi /tmp/oo", shell=True)
Upvotes: 3