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'sxlist(wait for an “exceptional condition”) - adding
close_fds=TruetoPopen - 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?