Reputation: 343
I am a teacher working on automating student assignment checking and would like the output file generated when I run a student program using subprocess.run
to look just like it does when I run it manually.
The following program
import subprocess
import sys
userInput = open("userInput.txt")
with open("output.txt", "w") as fout:
with open("error.txt", "w") as ferr:
result = subprocess.run(["python","hello.py"], stdin=userInput,stdout=fout,stderr=ferr)
with this hello.py
name = input("Your name: ")
age = input("Your age: ")
print('Hello,',name,"you are",age,"years old")
and this userInput.txt
Bob
27
generates the following output.txt
Your name: Your age: Hello, Bob you are 27 years old
How can I get the program to generate an output.txt file that looks the same as when I simply run hello.py (where the stdin also shows up in the stdout).
Your name: Bob
Your age: 27
Hello, Bob you are 27 years old
I am doing this on a Windows 10 system.
Upvotes: 2
Views: 368
Reputation: 341
This is a much more complicated task that it seems at first. You are basically trying to monitor the data being read from stdin and written to stdout.
The easy solution is to use the pexpect library. https://pexpect.readthedocs.io/en/stable/index.html
For example:
import pexpect
child = pexpect.spawn('./hello.py', logfile=open('output.txt', 'wb'))
child.expect('name:')
child.send('Bob\n')
child.expect('age:')
child.send('27\n')
child.expect('H') # Necessary to get the last line of output.
The output duplicates the stdin information. I'm not sure why it is doing that.
Your name: Bob
Bob
Your age: 27
27
Hello, Bob you are 27 years old
If you want to re-implement pexpect yourself, you will need to use subprocess.Popen()
and write to p.stdin
and read from p.stdout
directly.
This will require changing the p.stdout
to be non-blocking.
import os
import fcntl
import subprocess
p = subprocess.Popen(['hello.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
flags = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
fcntl.fcntl(p.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
while True:
try:
out = os.read(p.stdout.fileno(), 100000).decode("utf-8").strip()
# process output
except BlockingIOError:
time.sleep(0.1)
Upvotes: 1