Supplying input in subprocess and print the inputs along with the outputs (Python running Java)

Question:

I want to execute this java program using python

import java.util.Scanner;
public class Input {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Enter a number = ");
        int n = sc.nextInt();
        System.out.println("Number entered = "+n);
   }
}

I am taking a scanner input and do not want to take arguments using (String[] args).

I tried using

import subprocess
myInput = '1'
subprocess.run( [ 'java', 'Input.java' ], input=myInput.encode() )

for this python code, the output in terminal is

Enter a number = Number entered = 1

but the output I want is

Enter a number = 1
Number entered = 1

This question is similar to input to C++ executable python subprocess and
Capturing INPUT and output with subprocess (Python running java)
but there is no working solution. Is there an appropriate solution available now?

Asked By: Antonio

||

Answers:

problem is there is no platform independent method to do it, so if you are on linux you can set the process stdout to non-blocking and safely read from it, but on windows this is not possible, so there are 3 workarounds.

  1. modify the java file to print "n" after the question and receiver answer on different line, the python process can readline from stdout.
  2. know how many letters you want to read beforehand as if you are working with a socket.
  3. use threads

this solution implements the second option, by writing both stdin and stdout to another buffer for temporary storage.

import subprocess
import io


out_buff = io.StringIO()
with subprocess.Popen(['java', 'Input.java'],
                      stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,
                      text=True) as proc:

    out_buff.write(proc.stdout.read(17))
    in_val = "1n"
    out_buff.write(in_val)
    proc.stdin.write(in_val)
    proc.stdin.flush()  # required
    out_buff.write(proc.stdout.read())

print(out_buff.getvalue())

out_buff.close()
Enter a number = 1
Number entered = 1

obviously if you are expecting a certain keyword before you answer, you can keep reading letter by letter from stdout until you know you have to input something, but if you wanted to read more than you actually can then your application will block.
an option to avoid blocking is to use threads and this answer shows how to do so, but it gets complicated.

Edit: to show the third option is possible using only 1 extra thread, but you have to put in delays

import subprocess
import io
import threading
import time

def read_from_stdout(buffer,handle):
    try:
        while True:
            buffer.write(handle.read(1))
    except ValueError as e: # when reading from closed file
        pass

out_buff = io.StringIO()
with subprocess.Popen(['java', 'Input.java'],
                      stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,
                      text=True) as proc:

    thread = threading.Thread(target=read_from_stdout,args=(out_buff,proc.stdout))
    thread.start()
    time.sleep(0.1)  # wait for stdout to be read
    in_val = "1n"
    out_buff.write(in_val)
    proc.stdin.write(in_val)
    proc.stdin.flush()  # required
    time.sleep(0.1)  # wait for stdout to be read

print(out_buff.getvalue())

out_buff.close()
Answered By: Ahmed AEK
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.