views:

513

answers:

2

I've got an interactive program called my_own_exe. First, it prints out alive, then you input S\n and then it prints out alive again. Finally you input L\n. It does some processing and exits.

However, when I call it from the following python script, the program seemed to hang after printing out the first 'alive'.

Can anyone here tell me why this is happening?

// after reading the follow ups (thank you guys), i modified the code as following:

import subprocess
import time

base_command = "./AO_FelixStrategy_UnitTest --bats 31441 --chix 12467 --enxutp 31884 --turq 26372 --symbol SOGN --target_date " + '2009-Oct-16'
print base_command

proc2 = subprocess.Popen(base_command, shell=True , stdin=subprocess.PIPE,)

time.sleep(2);
print "aliv"
proc2.communicate('S\n')

print "alive"
time.sleep(6)

print "alive"
print proc2.communicate('L\n')
time.sleep(6)

the program now goes well with the first input 'S\n', but then stopped, and I the second 'L\n' is kinda ignored.

Can anyone give me an idea why it's like this?

thanks again

+3  A: 

From the docs for communicate:

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate.

So after communicate() runs, the process has been terminated.

If you want to write and read without waiting for the process to stop:

  • Don't ever use shell=True - it needlessy invokes a shell to in turn call your program, so there will be another process between you and your program. That has lots of unpleasant side-effects. The default is shell=False so you should stick with that. Change your Popen line to:

    p = subprocess.Popen(["./AO_FelixStrategy_UnitTest",
                          "--bats", "31441", "--chix", "12467",
                          "--enxutp", "31884", "--turq", "26372",
                          "--symbol", "SOGN", "--target_date", '2009-Oct-16'],
                         stdin=subprocess.PIPE, 
                         stdout=subprocess.PIPE)
    
  • Use p.stdin.write to write to the process. Use p.stdout.read to read from it.

  • Calling p.stdout.read if there's nothing to read will block. Calling p.stdin.write if the write buffer is full will block. So you have to make sure you have something to read/write - you do that on unix OS by using select. On windows you unfortunately must resort to threads. At least that is what Popen.communicate does internally.
  • If you didn't write AO_FelixStrategy_UnitTest then you have possible additional problems:
    • It could be reading from somewhere else, not standard input. Some programs read directly from the terminal, others use some OS API to read. That means data written to stdin won't go to the program. This is often true for password prompts.
    • Remember that you have to account for AO_FelixStrategy_UnitTest buffers. By default standard C PIPE communication is buffered so you may not see any output until after you've closed the input side (by doing p.stdin.close(). Unless AO_FelixStrategy_UnitTest flushes the output periodically.

Here's some example code, based on what you describe. It could work depending on how AO_FelixStrategy_UnitTest was developed:

p = subprocess.Popen(["./AO_FelixStrategy_UnitTest",
                      "--bats", "31441", "--chix", "12467",
                      "--enxutp", "31884", "--turq", "26372",
                      "--symbol", "SOGN", "--target_date", '2009-Oct-16'],
                     stdin=subprocess.PIPE, 
                     stdout=subprocess.PIPE)
output = p.communicate('S\nL\n')[0]
print output
nosklo
Isn't the process restarted every time he calls `proc2`? Or is not calling it since it has print in front of two of them? And if that's the case, why is freezing after the first print and not the second?
Anthony
@Anthony: No. The process is not reestarted. It is freezing after the first print because `communicate` is waiting for the process to end, but the process never ends because it is probably stuck on the second prompt (the one where `'L\n'` should be entered).
nosklo
thanks. anyway, this only partly solve the problem, i can use communicate only once, correct? if I need to INTERACTIVELY read and write and read and write, this "output = p.communicate('S\nL\n')[0]" could not work, correct?
ccfenix
@ccfenix: exactly. By using `.communicate()`, You write those stuff as soon as you can and close stdin, and keep listening on stdout until the process ends. If you want to interactively talk with the process, you need to do what I said about using `p.stdin.write` and `p.stdout.read` instead of `.communicate()` in the question. I also stated some caveats, like the part about buffering that can happen on the external executable.
nosklo
cool, very nice!~
ccfenix
+1  A: 

communicate() reads data from stdout and stderr until end-of-file is reached. - It waits until your program quits.

Messa
So what is the right method for a back-and-forth interaction with a subprocess?
Anthony
@Anthony: The correct way is to use `p.stdin.write()` and `p.stdout.read()`. However those can block, so you use `select()` on unix or threads on windows to avoid blocking. Check what `.communicate()` does by inspecting your `subprocess.py` source code. Check my answer for more details.
nosklo
It wasn't my question, I was just trying to generate something the SO might find helpful for fixing his code. That and I was genuinely curious, as I was working on something vaguely similar in PHP last weekend and I like to know how the better-half lives.
Anthony