tags:

views:

444

answers:

2

I'd like to execute multiple commands in a standalone application launched from a python script, using pipes. The only way I could reliably pass the commands to the stdin of the program was using Popen.communicate but it closes the program after the command gets executed. If I use Popen.stdin.write than the command executes only 1 time out of 5 or so, it does not work reliable. What am I doing wrong?

To elaborate a bit :

I have an application that listens to stdin for commands and executes them line by line. I'd like to be able to run the application and pass various commands to it, based on the users interaction with a GUI. This is a simple test example:

import os, string
from subprocess import Popen, PIPE

command = "anApplication" 
process = Popen(command, shell=False, stderr=None, stdin=PIPE)

process.stdin.write("doSomething1\n")
process.stdin.flush()
process.stdin.write("doSomething2\n")
process.stdin.flush()

I'd expect to see the result of both commands but I don't get any response. (If I execute one of the Popen.write lines multiple times it occasionally works.)

And if I execute:

process.communicate("doSomething1")

it works perfectly but the application terminates.

A: 

What you are trying to do is called a "coprocess".

What you are almost certainly missing is a stdin.flush(). Also, be sure to include the final newline your coprocess probably requires after each command.

Note that implementing coprocesses can be tricky, because it requires synchronization between the parent and the child process. Specifically, you need a way to tell when the subprocess has finished producing the output for each command.

Also, look out for the potential deadlock that can arise if you set the subprocess stdout and stderr to different pipes: if the subprocess fills the stderr it will block until you read stderr. So, either use the same pipe for stdout and stderr, or use a separate thread to read stderr.

ddaa
That sounds like what his problem is, but the OP's sample code has the application sending output to the normal everyday stdout and stderr, so there should be no deadlock.
Omnifarious
Actually, I wrote that answer before the OP added the code sample. So that was a shot in the dark.
ddaa
A: 

It sounds like your application is treating input from a pipe in a strange way. This means it won't get all of the commands you send until you close the pipe.

So the approach I would suggest is just to do this:

process.stdin.write("command1\n")
process.stdin.write("command2\n")
process.stdin.write("command3\n")
process.stdin.close()

It doesn't sound like your Python program is reading output from the application, so it shouldn't matter if you send the commands all at once like that.

Omnifarious
The problem with this solution, is that it executes all three commands at once, and not one by one, based on the users interaction with a GUI. Simply executing the commands work well with Popen.communicate but I'd like to be able to run them separately.
sz
@sz, I suspect this is the application's problem. And I suspect that in order to fix it you'd have to go to the trouble of opening a pseudo-terminal, which is a type of virtual device in Unix/Linux. It allows one program to pretend to be a terminal for another program. If you are on Unix/Linux, you can test to make sure it's the application's problem and not Python's be having the application be `"cat -u"` and seeing if the lines come out when Python writes them.
Omnifarious