1

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.

hb20007
  • 515
  • 1
  • 9
  • 23
  • 1
    try changing the ```p.stdin.readline()``` commands to ```p.stdin.read()```. You may even want to put it in a loop, as ```while (print(p.stdin.read())): next ``` to ensure that anything that needs reading gets read. Not sure if that's the issue, but it will test your theory about the readline() functionality. – Fubar May 22 '20 at 14:22

1 Answers1

1

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

Answer

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 same directory. If your program is executed successfully then output.txt contains output of code test.py but if your code gives any error then error is in output.txt.

Answer As You Want

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())
Ruben Helsloot
  • 12,582
  • 6
  • 26
  • 49
PSKP
  • 1,178
  • 14
  • 28
  • This works if I have `1\n2` in `input.txt`. It's a great solution if the input is fixed. – hb20007 May 22 '20 at 14:31
  • Yeah Its because Your program is reading 2 integers separately. Your input file contains same data which you give to your program through console. If you got expected results Please Accept the answer – PSKP May 22 '20 at 14:32
  • The only problem with this answer is that I don't want to have my input in a file. I want to pass it through the program itself. So let's say I have `firstParam = 1`, and `secondParam = 2`. Is there a way to use this as the input instead of reading from `input.txt`? – hb20007 May 22 '20 at 14:37
  • If your input is static (not changing runtime) then you can write it to file and then run above code but if your input is changing then it is not useful at all. – PSKP May 22 '20 at 14:40
  • So let's say I have `firstParam` and `secondParam` as variables and I have calculated their values already. I can save their values into a file, run your code, and then delete the file. But is there a better way to do it, perhaps using a different `command`? – hb20007 May 22 '20 at 14:43
  • Yeah you are right! I am also finding solution to this problem as you want. – PSKP May 22 '20 at 14:50
  • I got answer as you want. added to answer keeping original. Please check and Accept it. – PSKP May 22 '20 at 15:34
  • Unfortunately, your updated answer is not what I want. Please check the last line of my question. I cannot modify `test.py`. I want `test.py` to stay like it is now. – hb20007 May 22 '20 at 15:44
  • Luckily I just copied and pasted your `test.py` as it is and what? it worked. only change `main.py` and check – PSKP May 22 '20 at 15:56
  • Thanks, this works. `p.stdin.flush()` prevents the program from freezing. – hb20007 May 22 '20 at 16:18
  • Is there any reason why you don't just recommend ``.communicate``? – MisterMiyagi May 25 '21 at 09:07
  • https://stackoverflow.com/a/16769956/8507296 this is why – PSKP May 25 '21 at 09:13