Search code examples
pythonpython-3.xsubprocess

How to interact with an external program in Python 3?


Using Python 3, I want to execute an external program, interact with it by providing some text into standard input, and then print the result.

As an example, I created the following external program, called test.py:

print('Test Program')
print('1 First option, 2 Second Option')

choice = input()

if choice == '1':
    second_param = input('Insert second param: ')
    result = choice + ' ' + second_param
        
    print(result)

If I run this program directly, it works as expected. If I provide the input 1 and then 2, the result is 1 2.

I want to run this program in another script and interact with it to print the same result.

After reading the documentation for subprocess, and checking out similar questions on SO, I ended up with the following:

EXTERNAL_PROG = 'test.py'

p = Popen(['py', EXTERNAL_PROG], stdout=PIPE, stdin=PIPE, shell=True)

print(p.stdout.readline().decode('utf-8'))
print(p.stdout.readline().decode('utf-8'))
p.stdin.write(b'1\n')
p.stdin.write(b'2\n')
print(p.stdout.readline().decode('utf-8'))

However, when I run the code, the program freezes after printing 1 First option, 2 Second Option, and I need to restart my shell. This is probably caused by the fact that subprocess.stdout.readline() expects to find a newline character, and the prompt for the second param doesn’t contain one.


I found 2 SO questions that talk about something similar but I couldn’t get it to work.

Here, the answer recommends using the pexpect module. I tried to adapt the code to my situation but it didn’t work.

Here, the suggestion is to use -u, but adding it didn’t change anything.


I know that a solution can be found by modifying test.py, but this is not possible in my case since I need to use another external program and this is just a minimal example based on it.


Solution

  • If you have fixed input to your program (means input not changing at run time) then this solution can be relevant.

    Answer (Fixed Input)

    First create file.

    • Input file. name it input.txt and put 1 2 in it
    
    command = "python test.py < input.txt > output.txt 2>&1"
    
    # now run this command
    
    os.system(command)
    

    When you run this, you will find output.txt in the same directory. If your program is executed successfully then output.txt contains the output of code test.py but if your code gives any error then error is in output.txt.

    Answer (Variable Input)

    Better solution where you can easily control input and output of external program/process.

    main.py become

    import sys
    from subprocess import PIPE, Popen
    
    EXTERNAL_PROG = 'test.py'
    
    p = Popen(['python3', EXTERNAL_PROG], stdout=PIPE, stdin=PIPE, stderr=PIPE)
    
    print(p.stdout.readline())
    print(p.stdout.readline())
    p.stdin.write(b'1\n')
    p.stdin.write(b'2\n')
    p.stdin.flush()
    print(p.stdout.readline())
    print(p.stdout.readline())