I need a way to either read all currently available characters in stream created by Popen or to find out how many characters are left in the buffer.
Backround: I want to remote control an interactive application in Python. So far I used Popen to create a new subprocess:
process=subprocess.Popen(["python"],shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, cwd=workingDir)
(I'm not really starting python, but the actual interactive interface is similar.) At the moment I read 1 byte until I detect that the process has reached the command prompt:
output = ""
while output[-6:]!="SCIP> ":
output += process.stdout.read(1)
sys.stdout.write(output[-1])
return output
Then I start a lengthy computation via process.stdin.write("command\n")
.
My problem is, that I cannot check whether the computation has finished or not, because I cannot check, whether the last characters in the stream are the prompt or not. read()
or read(n)
blocks my thread until it reaches EOF, which it never will, because the interactive program will not end until it is told to. Looking for the prompt in the way the above loop does won't work either, because the prompt will only occur after the computation.
The ideal solution would allow me to read all available character from the stream and immediately return an empty string, if there is nothing to read.
I don't think that readline() will block your process.
Earlier I tried to use
but that seems to hang until the process terminates.
It is not correct that read() blocks till EOF - it blocks until it gets enough data that it needs - and from the other side is possible that some data is kept in the buffers (it's not flushed just because you ended print with new line).
Why not try in the child printing something like
"### OVER ###\n"
and thenstdout.flush()
, then on parent side collect til you see the OVER token, say with''.join(i for i in iter(process.stdout.readline, '### OVER ###\n'))
I've tried a lot of approaches like making a non-blocking
stdout
by the following:But the only working solution is described here:
And then:
Incremental parsing of Popen's stdout is not a problem really. Just insert a pipe into a thread and have it scrub through output, looking for delimiters. Depending on your preference, it can pipe it into another pipe / file-like or put the parsed "chunks" on the "stack" in asynchronous mode. Here is an example of asynchronous "chunking" of stdout based on custom delimiter:
Edit: removed the erroneous verbiage about "writing to proc's stdin is a one-time-thing"
There's another possible solution, but it may require that you rearrange your program a bit.
If you have multiple sources of I/O (file descriptors, sockets, etc.), and you want to wait on all of them at once, use the Python select module. You could (for instance) put standard input (for reading from the terminal) and the pipe (from the subprocess) in a list, and wait for input to be ready on either one of them.
select
blocks until I/O is available on any of the descriptors in the list. You then scan the list, looking for the ones that have available data.This approach turns out to be quite efficient--much more so than polling a file descriptor to see if there's any data. It also has the virtue of simplicity; that is, you can accomplish what you want with a minimum of code. Simpler code means fewer opportunities for bugs.
Poking around I found this really nice solution
Persistent python subprocess
which avoids the blocking issue all together by using
fcntl
to set file attributes on the subprocess pipes to non-blocking mode, no auxiliary threads or polling required. I could be missing something but it has solved my interactive process control problem.