86

I have a script named 1st.py which creates a REPL (read-eval-print-loop):

print "Something to print"
while True:
    r = raw_input()
    if r == 'n':
        print "exiting"
        break
    else:
        print "continuing"

I then launched 1st.py with the following code:

p = subprocess.Popen(["python","1st.py"], stdin=PIPE, stdout=PIPE)

And then tried this:

print p.communicate()[0]

It failed, providing this traceback:

Traceback (most recent call last):
  File "1st.py", line 3, in <module>
    r = raw_input()
EOFError: EOF when reading a line

Can you explain what is happening here please? When I use p.stdout.read(), it hangs forever.

payne
  • 13,833
  • 5
  • 42
  • 49
Black_Hat
  • 943
  • 1
  • 9
  • 12

3 Answers3

73

.communicate() writes input (there is no input in this case so it just closes subprocess' stdin to indicate to the subprocess that there is no more input), reads all output, and waits for the subprocess to exit.

The exception EOFError is raised in the child process by raw_input() (it expected data but got EOF (no data)).

p.stdout.read() hangs forever because it tries to read all output from the child at the same time as the child waits for input (raw_input()) that causes a deadlock.

To avoid the deadlock you need to read/write asynchronously (e.g., by using threads or select) or to know exactly when and how much to read/write, for example:

from subprocess import PIPE, Popen

p = Popen(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1)
print p.stdout.readline(), # read the first line
for i in range(10): # repeat several times to show that it works
    print >>p.stdin, i # write input
    p.stdin.flush() # not necessary in this case
    print p.stdout.readline(), # read output

print p.communicate("n\n")[0], # signal the child to exit,
                               # read the rest of the output, 
                               # wait for the child to exit

Note: it is a very fragile code if read/write are not in sync; it deadlocks.

Beware of block-buffering issue (here it is solved by using "-u" flag that turns off buffering for stdin, stdout in the child).

bufsize=1 makes the pipes line-buffered on the parent side.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 3
    can you tell me what is the difference between print >>p.stdin,i and p.stdin.write(i) – Black_Hat May 27 '13 at 10:02
  • 3
    here `print` is `p.stdin.write(str(i)+"\n"); p.stdin.flush()`. – jfs May 27 '13 at 10:19
  • 1
    thank you... One more thing please tell me what this bufsize=1 is doing? Also "-u" in Popen(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1) – Black_Hat May 27 '13 at 12:03
  • I've updated the answer. [Read the first link on why the buffering matters](http://stackoverflow.com/q/443057/4279) – jfs May 27 '13 at 12:40
  • 2
    I want to continually talk a process, sending it commands constantly and receiving its response constantly. But this process is always alive. How does one do this? – Charlie Parker Feb 24 '19 at 20:53
  • @CharlieParker look at [the link I gave you yesterday](https://stackoverflow.com/a/34572974/4279) – jfs Feb 25 '19 at 14:55
27

Do not use communicate(input=""). It writes input to the process, closes its stdin and then reads all output.

Do it like this:

p=subprocess.Popen(["python","1st.py"],stdin=PIPE,stdout=PIPE)

# get output from process "Something to print"
one_line_output = p.stdout.readline()

# write 'a line\n' to the process
p.stdin.write('a line\n')

# get output from process "not time to break"
one_line_output = p.stdout.readline() 

# write "n\n" to that process for if r=='n':
p.stdin.write('n\n') 

# read the last output from the process  "Exiting"
one_line_output = p.stdout.readline()

What you would do to remove the error:

all_the_process_will_tell_you = p.communicate('all you will ever say to this process\nn\n')[0]

But since communicate closes the stdout and stdin and stderr, you can not read or write after you called communicate.

User
  • 14,131
  • 2
  • 40
  • 59
  • thanks for reply..but you can see my first program is waiting for user input and before that it is printing "Something to print" . So what I want is: from the 2nd process I just want to read that line ("Something to print"). and later from this (2nd process) I want to write to its stdin and then again want to read whatever 1st process (child process) is writing to the stdout. So, tell me how can I do that? – Black_Hat May 27 '13 at 09:26
  • I commented the code. That should now do what you want. Is it clearer? – User May 27 '13 at 09:32
  • my parent process hangs (stops forever) if I use p.stdout.read()/readline() when child process is waiting for user input. – Black_Hat May 27 '13 at 09:37
  • you could [use `"-u"` flag and `p.stdin.flush()` (or `bufsize=1`) to fix the first example](http://stackoverflow.com/a/16770371/4279). +1 for the second example (it is preferable if the input doesn't depend on the output). – jfs May 27 '13 at 10:15
0

Your second bit of code starts the first bit of code as a subprocess with piped input and output. It then closes its input and tries to read its output.

The first bit of code tries to read from standard input, but the process that started it closed its standard input, so it immediately reaches an end-of-file, which Python turns into an exception.

icktoofay
  • 126,289
  • 21
  • 250
  • 231
  • thanks... but I just don't want to close any input. I want to print wherever the 1st.py is printing to its stdout. Later, I will do p.stdin.write("Somthing to input") and then p.communicate()[0] to print this in 2nd code. Can you help me? I just don't want to close the input or output. I am novice, so don't hesitate to correct me. thanks – Black_Hat May 27 '13 at 08:08