Additional keypress needed after executing command in pseudoterminal

24 Views Asked by At

Motivation:

I want to run external command (which uses lot of fancy terminal commands) from my program using pseudo terminal and regain control immediately after the external command ends.

Problem:

It works fine, but after the command exits, it hangs until I press additional key (enter, escape, space - all works).

Minimal working example:

This is minimal (or as minimal as I was able to make it) working example of my problem using ls | less as the external program (in "the real" code it uses something else):

import os, pty, termios, sys, select, tty
from subprocess import Popen

original_tty_settings = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())
try:
        master_fd, slave_fd = pty.openpty()
        p = Popen(
            'ls | less',
            preexec_fn          = os.setsid,
            stdin               = slave_fd,
            stdout              = slave_fd,
            stderr              = slave_fd,
            universal_newlines  = True,
            shell               = True,
        )
        while p.poll() is None:
            r, w, e = select.select([sys.stdin, master_fd], [], [])
            if sys.stdin in r:
                data_in = os.read(sys.stdin.fileno(), 1024)
                os.write(master_fd, data_in)
            elif master_fd in r:
                data_out = os.read(master_fd, 1024)
                if data_out:
                    os.write(sys.stdout.fileno(), data_out)
finally:
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_tty_settings)
print('Resuming')

What I expect to happen:

I expect the program runs ls | less, I scroll, I press q, the ls ends and my program prints "Resuming" and ends

What happens instead:

I run the program, it runs ls | less, I scroll, I press q and nothing happens until additional keypress, then it prints "Resuming" and ends.

What I tried:

  • adding all file descriptors to select's xlist (wait for an “exceptional condition”)
  • adding close_fds=True to Popen
  • I feel the main problem is blocking os.read() call, but I was unable to make it non-blocking

Question:

How can I remove the need for additional keypress, after the external program ends?

0

There are 0 best solutions below