bugen / pypipe

Python pipe command line tool
Apache License 2.0
796 stars 24 forks source link

Issue with Terminal Display Corruption upon Ctrl-C Interruption when Pager is Enabled #25

Open bugen opened 10 months ago

bugen commented 10 months ago

When the pager is enabled, interrupting with Ctrl-C causes a problem where the terminal display becomes corrupted (command input is no longer visible). Although this issue has been mitigated by handling SIGINT to ignore it when the pager is active (1bf05212afc15ff1e3b42bc26956a5151bb3c2f4), a fundamental solution has not been achieved.

Conditions for occurrence in my environment:

Steps to reproduce:

  1. Comment out the following line in pypipe.py:
    signal.signal(signal.SIGINT, signal.SIG_IGN)
  2. Execute the following command:
    cat docs/staff.txt | PYPIPE_PAGER_ENABLED=true PYPIPE_PAGER='less' ppp rec
  3. Interrupt with Ctrl-C while viewing in less.

The issue should be reproducible with the above steps. If you change docs/staff.txt to a larger file (around 1000 lines) and perform the same steps, the problem does not occur. However, even with large data, if you press Ctrl-C after loading the entire file with Gin less, the issue should occur.

Is there anyone who understands this behavior?

diazona commented 10 months ago

Ah, now I see why you wanted the -K option :smile:

I wouldn't say I fully understand what's going on here, but this is something known to happen in some cases when a program that has put the terminal in non-echo mode is abruptly terminated. echo mode just means that every keystroke received is immediately processed by the terminal and leads to a character appearing on the screen, while non-echo mode is the opposite, keystrokes do not immediately lead to characters appearing on the screen but are handed off to the running program for processing. You can manually turn echo mode off by running stty -echo, and then to turn it on again, run stty echo (note that you'll have to type that command without seeing it show up on the screen, but it still runs). Or reset will work too; it completely reinitializes your terminal back to default settings, including turning echo mode on. None of this is really related to pypipe; given some time, I bet I could find a way to reproduce the effect you are seeing without involving pypipe at all.

I'm sure other programs that use pagers have their own ways of handling this sort of thing, and perhaps you could look at one of them for inspiration. Off the top of my head, a couple things I would look into:

Hopefully someone more knowledgeable about using a pager as a subprocess can come along with better ideas. (Or if I think of anything else, I'll let you know.)

bugen commented 10 months ago

@diazona You are geek! stty -echo I've been using the terminal almost every day for a long time, but I've never used a command like this. 😲 After an issue occurred, I confirmed that it could be resolved by executing stty echo or tset or reset (although it is not visible on the screen).

My hypothesis is that less might need its standard input to be open in order to reset the terminal into echo mode, and by prematurely closing it, you are preventing less from restoring terminal settings when it exits

It indeed seems that the issue is arising from less not terminating correctly. When terminating pypipe, I am calling proc.stdin.close() (where proc refers to the pager launched with subprocess.Popen, and proc.stdin is the pipe). I'll investigate this aspect a bit more.

Or, put less in its own process group,

I was just looking into 'process group'. I'll check if it's possible to control process group in python's subprocess.

I feel like I've made significant progress toward a solution. Thank you.