rainerpm
rainerpm

Reputation: 343

How to combine stdin with stdout?

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

Answers (1)

egrubbs
egrubbs

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

Related Questions