1

I'm writing an IDE for python, in python, and need to use subprocess to intereact with a user's script.

I am completely new to using subprocess and not sure what I'm doing here. I've created a test snippet representing what I'm trying to do:

from subprocess import Popen,PIPE,STDOUT
import tkinter as tk

t=tk.Text()
t.pack()

p = Popen(["python","c:/runme.py"],stdout=PIPE,stdin=PIPE,stderr=PIPE,shell=True)
p.stdin.write("5".encode())
out=p.stdout.read()

t.insert(1.0,out)

And here is the test script I'm trying to interact with:

print("Hello World")
inp=input("Enter a Number: ")
print(inp)
quit()

Unfortunately it just waiting (presumably) on line 2. How do I read what has already been printed and how to I then input the string?

Daniel
  • 42,087
  • 4
  • 55
  • 81
  • Possible duplicate of [Constantly print Subprocess output while process is running](https://stackoverflow.com/questions/4417546/constantly-print-subprocess-output-while-process-is-running) – tripleee Jul 13 '18 at 13:47
  • Running Python as a subprocess of Python is just crazy. Look into the `multiprocessing` module if you genuinely need Python to execute multiple processes in parallel. – tripleee Jul 13 '18 at 13:49

3 Answers3

2

You have to flush stdout regularly, because, if the script is not connected to a terminal, the output is not automatically flushed:

import sys
print("Hello World")
print("Enter a Number: ")
stdout.flush()
inp = input()
print(inp)

and you have to terminate the input by return \n:

p = Popen(["python", "c:/runme.py"], stdout=PIPE, stdin=PIPE, stderr=PIPE)
p.stdin.write("5\n".encode())
out = p.stdout.read()
Daniel
  • 42,087
  • 4
  • 55
  • 81
  • The `stdout` lies in `sys` module, and the `p.stdin` should also be flushed after `write()`, too. All other parts of my answer is same as yours :) – Cychih Aug 09 '16 at 19:33
2

Remove shell=True. Currently you are not executing the script at all, but just launching a python interactive interpreter.

The problem is that when you use shell=True the way in which the first argument is interpreted changes. You do not need shell=True and the arguments you provided are correct for the shell=False version.

See the difference between:

>>> import subprocess
>>> subprocess.Popen(['python', 'whatever'], shell=True)
<subprocess.Popen object at 0x7ff1bf933d30>
>>> Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
KeyboardInterrupt
>>> 
KeyboardInterrupt

Which as you may notice launches a python interpreter which gets stuck, and this:

>>> import subprocess
>>> subprocess.Popen(['python', 'whatever'])
<subprocess.Popen object at 0x7f14e1446cf8>
>>> python: can't open file 'whatever': [Errno 2] No such file or directory

Which tries to execute whatever.


Also you should consider using the communicate method instead of reading and writing directly to/from stdin/stdout.

Community
  • 1
  • 1
Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • I have removed `shell=True` and flush before reading and after writing. Now my problem is that I cannot read what the input prompt is, I have to write before I can read. Is there a way of reading while the file is waiting for input, then "forwarding" user input from that file to the process? – Hayden Ravenscroft Aug 10 '16 at 10:57
  • @HaydenR The problem could be that the prompt isn't flushed by `input`. Try to change `input(prompt)` with `sys.stdout.write(prompt);sys.stdout.flush();input()` and see if that makes a difference. If it does you should write a simple wrapper around `input` that flushes the prompt. – Bakuriu Aug 10 '16 at 18:26
0

I know this is a bit late, but I hope this helps anyone who has a similar problem now.

To send inputs from one python file to another (python version 3.7), I used three files.

  1. File for running the subprocess
  2. File for outputs (very simple)
  3. File that needs the inputs

Here are the three files in the same order as above.

You don't need to print out the output, but I'll include the terminal output below the file examples. The subprocess file:

from subprocess import Popen,PIPE

p1 = Popen(["python","output_file.py"], stdout=PIPE)
p2 = Popen(["python", "input_file.py"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()

output = p2.communicate()[0]

print(output)

The output file is very simple and there may be a way to work around it. Nevertheless, here is my version:

print(1)
print(2)
print('My String')

The input file requires type casting for numbers.

i = input('Enter a number: ')
j = input('Enter another: ')
k = int(i) + int(j)
print(k)
l = input('Tell me something. ')
print(l)

Here is the terminal output:

b'Enter a number: Enter another: 3\r\nTell me something. My String!\r\n'