To start, I'm aware this looks like a duplicate. I've been reading:
Python subprocess readlines() hangs
Python Subprocess readline hangs() after reading all input
subprocess readline hangs waiting for EOF
But these options either straight don't work or I can't use them.
The Problem
# Obviously, swap HOSTNAME1 and HOSTNAME2 with something real
cmd = "ssh -N -f -L 1111:<HOSTNAME1>:80 <HOSTNAME2>"
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ)
while True:
out = p.stdout.readline()
# Hangs here ^^^^^^^ forever
out = out.decode('utf-8')
if out:
print(out)
if p.poll() is not None:
break
My dilemma is that the function calling the subprocess.Popen()
is a library function for running bash commands, so it needs to be very generic and has the following restrictions:
- Must display output as it comes in; not block and then spam the screen all at once
- Can't use multiprocessing in case the parent caller is multiprocessing the library function (Python doesn't allow child processes to have child processes)
- Can't use
signal.SIGALRM
for the same reason as multiprocessing; the parent caller may be trying to set their own timeout - Can't use third party non-built-in modules
- Threading straight up doesn't work. When the
readline()
call is in a thread,thread.join(timeout=1)
lets the program continue, but ctrl+c doesn't work on it at all, and callingsys.exit()
doesn't exit the program, since the thread is still open. And as you know, you can't kill a thread in python by design. - No manner of bufsize or other subprocess args seems to make a difference; neither does putting readline() in an iterator.
I would have a workable solution if I could kill a thread, but that's super taboo, even though this is definitely a legitimate use case.
I'm open to any ideas.