Send input to python subprocess without waiting fo

2019-07-16 14:51发布

问题:

I'm trying to write some basic tests for a piece of code that normally accepts input endlessly through stdin until given a specific exit command.

I want to check if the program crashes on being given some input string (after some amount of time to account for processing), but can't seem to figure out how to send data and not be stuck waiting for output which I don't care about.

My current code looks like this (using cat as an example of the program):

myproc = subprocess.Popen(['cat'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

myproc.communicate(input=inputdata.encode("utf-8"))
time.sleep(0.1)

if myproc.poll() != None:
    print("not running")
else:
    print("still running")

How can I modify this to allow the program to proceed to the polling instead of hanging after the communicate() call?

回答1:

You are using the wrong tool here with communicate which waits for the end of the program. You should simply feed the standard input of the subprocess:

myproc = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)
myproc.stdin.write(inputdata.encode("utf-8"))

time.sleep(0.1)

if myproc.poll() != None:
    print("not running")
else:
    print("still running")

But beware: you cannot be sure that the output pipes will contain anything before the end of the subprocess...



回答2:

so I think I understand what you want here. If you know an existing command that will crash your program you can use ‘subprocess.Popen.wait()’ and it’ll still block but it’ll return a tuple of the output message and the error associated with it if any.

Then you can note the error and catch it in a try exception statement.

This was really helpful when I was working with sub processes: https://docs.python.org/3/library/asyncio-subprocess.html



回答3:

You could set a timeout in the Popen.communicate(input=None, timeout=None) function. After the timeout the process is still running and I think but you have to test it you can still send in input with communicate.

From the docs:

If the process does not terminate after timeout seconds, a TimeoutExpired exception will be raised. Catching this exception and retrying communication will not lose any output.

The child process is not killed if the timeout expires, so in order to cleanup properly a well-behaved application should kill the child process and finish communication: