1

All of the examples I see for interacting with a process using Python 3's subprocess.Popen use Popen.communicate("input_text") exactly once before calling Popen.communicate() to grab the standard output and ending the program. I have a few programs that I want to script that require human intervention via stdin, so I want to automate them since the prompts are predictable.

For example, an in-house licensing application requires us to pass the application information via prompts (not from the command line) relating to the customer's unique ID (4 digit integer), the number of users, etc. And then it has to be done 30 times (random number), each one for a different product, identified by another integer.

Scripting that is easy if only I can learn how to do a sustained back-and-forth using Popen. Should I be using Popen.communicate() or should I be using Popen.stdout and Popen.stdin() and what's the difference between both?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
UtahJarhead
  • 2,091
  • 1
  • 14
  • 21
  • Do you know at launch time the full set of inputs the program will need, and can you wait to process results until the end? If so, stick with `communicate`, and feed it everything at once; it's simpler, and you don't risk deadlocks. If you can't predict the prompts, or must process stuff live, you're stuck with uglier code using either threads or the `select`/`selectors` module to avoid deadlocks. – ShadowRanger Dec 18 '18 at 20:50

2 Answers2

1

I know this is late, but here is an example of two modules communication back-and-forth, if you will. The Odds.py module prints the odd numbers, and the Evens.py module prints the even numbers. An integer argument is passed through Popen, and a number is printed depending on the argument. It is pretty straight forward how this is accomplished - hope it helps.

File1 (Odds.py)

from subprocess import Popen
import sys

class Odds():
    def PrintNums():
        print("1")
        Evens = Popen([sys.executable, 'Evens.py', "1"])
        Evens.wait()

        print("3")
        Evens = Popen([sys.executable, 'Evens.py', "2"])
        Evens.wait()

        print("5")
        Evens = Popen([sys.executable, 'Evens.py', "3"])
        Evens.wait()

    PrintNums()

File2 (Evens.py)

import sys

class Evens():
    def __init__(self, num):
        self.num = num

    def PrintNums(self):
        if self.num == "1":
            print("2")

        elif self.num == "2":
            print("4")

        elif self.num == "3":
            print("6")

if len(sys.argv) > 1:
    obj = Evens(sys.argv[1])
    obj.PrintNums()

Output

1
2
3
4
5
6
newby73
  • 45
  • 11
0

Popen.communicate will block until the subprocess has completed or failed and only then returns information from stdout and stderr. So this is not what you need.

stdin, stdout & stderr are essentially special files belonging to a process that you can read from or write to, same as any other file, but they can provide an interface between processes if you pipe information into them.

I have recently had to implement something similar to what you described, the only way I was able to retrieve information through stdout of the "client" process was by using the pty module. I will link you two answers that helped me, however please note that these solutions are Posix only and using shell=True is a security risk. https://stackoverflow.com/a/5413588/533362 & https://stackoverflow.com/a/13605804/3565382.

tmccaffrey
  • 31
  • 4