Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Python's subprocess and Popen in one script to run another Python script which requires user interaction (by raw_input)

The problem I have is as follows, and I will use simple example to illustrate it. I have written a python script that requires user interaction, specifically it uses the raw_input() function to get the user's input. The code below simply asks the user to type in two numbers in succession (hitting enter between each), and returns the answer (surprise, surprise, it is called 'sum_two_numbers.py'). Ho-hum!

#! /usr/bin/python

#  -------------------
#  sum_two_numbers.py
#  -------------------
#  This script asks the user for two numbers and returns the sum!

a = float(raw_input("Enter the first number:"))
b = float(raw_input("Enter the second number:"))

print a+b

Now, I want to write a separate python script that executes the above script and 'feeds' the two necessary numbers to it. I hence call this script 'feeder.py'. I tried to write this script using Python's 'subprocess' module, specifically using the 'Popen' class and its associated 'communicate' method. Below is the script trying to feed the numbers '5' and '4'.

#! /usr/bin/python

#  ----------
#  feeder.py
#  ----------
import subprocess

child = subprocess.Popen("./sum_two_numbers.py",stdin=subprocess.PIPE)

child.communicate("5")
child.communicate("4")

This code doesn't work, and returns the errors upon execution:

$ ./feeder.py
Enter the first number:Enter the second number:Traceback (most recent call last):
  File "./sum_two_numbers.py", line 6, in <module>
    b = float(raw_input("Enter the second number:"))
EOFError: EOF when reading a line
Traceback (most recent call last):
  File "./feeder.py", line 8, in <module>
    child.communicate("4")
  File "/usr/lib/python2.7/subprocess.py", line 740, in communicate
    self.stdin.write(input)
ValueError: I/O operation on closed file

I'm don't know how to write the 'feeder.py' so that it will do what I want, these errors keep hindering me. I suspect that this error arises because of the following comment in the documentation:

Popen.communicate(input=None)

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

I'm not sure what to make of this sentence, and how it can help me...

Can anyone help me with making the above script work i.e. how to use subprocess and Popen properly... Or just how to write a 'feeder' script - in any (not too obscure) language! I have tried Pexpect, Expect but ran into problems like it not outputting the the child code's requests for input and me just generally having no idea what its doing.

like image 832
Joshua T Avatar asked Oct 19 '12 18:10

Joshua T


People also ask

How do I use subprocess Popen in Python?

The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly. Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.

Should I use Popen or subprocess?

The main difference is that subprocess. run() executes a command and waits for it to finish, while with subprocess. Popen you can continue doing your stuff while the process finishes and then just repeatedly call Popen. communicate() yourself to pass and receive data to your process.

What is the difference between subprocess call and Popen?

Popen is more general than subprocess. call . Popen doesn't block, allowing you to interact with the process while it's running, or continue with other things in your Python program. The call to Popen returns a Popen object.

How does subprocess Popen work?

The subprocess module defines one class, Popen and a few wrapper functions that use that class. The constructor for Popen takes arguments to set up the new process so the parent can communicate with it via pipes. It provides all of the functionality of the other modules and functions it replaces, and more.


1 Answers

You can only call communicate once. Therefore you need to pass all the input at once, i.e. child.communicate("1\n1\n"). Alternatively you can write to stdin:

child = subprocess.Popen("./test.py", stdin=subprocess.PIPE)         

child.stdin.write("1\n")                                                       
child.stdin.write("1\n")
like image 100
Rob Wouters Avatar answered Sep 28 '22 12:09

Rob Wouters