3

I am trying to make a full-fledged Java program that runs a python program. The python program is as follows:

print('Enter two numbers')
a = int(input())
b = int(input())
c = a + b
print(c)

If I execute this code, the terminal looks something like this:

Enter two numbers
5
3
8

Now, I want the same output when executing this code from Java. Here is my Java code:

import java.io.*;
class RunPython {
    public static void main(String[] args) throws IOException {
        String program = "print('Enter two numbers')\na = int(input())\nb = int(input())\nc = a + b\nprint(a)\nprint(b)\nprint(c)";
        FileWriter fileWriter = new FileWriter("testjava.py");
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
        bufferedWriter.write(program);
        bufferedWriter.close();
        Process process = Runtime.getRuntime().exec("python testjava.py");
        InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(process.getOutputStream());
        InputStreamReader read = new InputStreamReader(System.in);
        BufferedReader in = new BufferedReader(read);
        String output;
        while (process.isAlive()) {
            while (!bufferedReader.ready());
            System.out.println(bufferedReader.ready());
            while (!(output = bufferedReader.readLine()).isEmpty()) {
                System.out.println(output);
            }
            bufferedReader.close();
            if (process.isAlive()) {
                outputStreamWriter.write(in.readLine());
            }
        }
    }
}

But while running this program, only the first line is displayed and the first input is taken. After that, the program does not respond. What mistake am I making? And what is the solution?

Samudra Ganguly
  • 637
  • 4
  • 24
  • Have you stepped through your program with the debugger? – tgdavies Jul 27 '21 at 10:59
  • Try to write the Python code in the testjava.py and execute it without String Program – Decapitated Soul Jul 27 '21 at 11:07
  • You need to write to the process input stream in the same way as 'manual' input and have stdout and stderr able to respond asynchronously if you want to avoid IO blocking side effects. If you arrange it that way correctly, your problem should disappear – g00se Jul 27 '21 at 11:34
  • Have you looked into Jython for the solution? If not you can use something like `Process p = new ProcessBuilder("python", "myScript.py", "firstargument").start();` in order to pass arguments from java into the python script. But Jython can do both in a much clearer way regardless. – Eno Gerguri Jul 27 '21 at 11:47

2 Answers2

3

Dealing with Input and Output to another process is a bit messy and you can read a good answer to how you could do it here

So applying those answers to your code could be something like this:

import java.io.*;
import java.util.Scanner;

class RunPython {
    public static void main(String[] args) throws IOException {
//        String program = "print('Enter two numbers')\na = int(input())\nprint(a)\nb = int(input())\nprint(b)\nc = a + b\nprint(c)";
        // If you are using Java 15 or newer you can write code blocks
        String program = """
                print('Enter first number')
                a = int(input())
                print(a)
                print('Enter second number')
                b = int(input())
                print(b)
                c = a + b
                print(c)
                """;
        FileWriter fileWriter = new FileWriter("testjava.py");
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
        bufferedWriter.write(program);
        bufferedWriter.close();

        Process process =
                new ProcessBuilder("python", "testjava.py")
                        .redirectErrorStream(true)
                        .start();

        Scanner scan = new Scanner(System.in);

        BufferedReader pythonOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
        BufferedWriter pythonInput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

        Thread thread = new Thread(() -> {
            String input;
            while (process.isAlive() && (input = scan.nextLine()) != null) {
                try {
                    pythonInput.write(input);
                    pythonInput.newLine();
                    pythonInput.flush();
                } catch (IOException e) {
                    System.out.println(e.getLocalizedMessage());
                    process.destroy();
                    System.out.println("Python program terminated.");
                }
            }
        });
        thread.start();

        String output;
        while (process.isAlive() && (output = pythonOutput.readLine()) != null) {
            System.out.println(output);
        }
        pythonOutput.close();
        pythonInput.close();
    }
}

Note that the pythonInput is a BufferedWriter in Java and vice versa the pythonOutput is a BufferedReader.

Anders Lindgren
  • 338
  • 3
  • 9
1

Using Process:

Process p = new ProcessBuilder(
    "python", "myScript.py", "firstargument Custom input to a Python program from a Java program"
).start();

You can add as many arguments as you want when running it.


Using Jython (Easier option):

//Java code implementing Jython and calling your python_script.py
import org.python.util.PythonInterpreter;
import org.python.core.*;

public class ImportExample {
   public static void main(String [] args) throws PyException
   {
       PythonInterpreter pi = new PythonInterpreter();
       pi.execfile("path_to_script\\main.py");
       pi.exec("Any custom input from this java program in python to be run");
   }
}

Which embeds the Jython and can be read about here.

Note: you may have to initialize your interpreter with your python.path if you want to use any packages you have installed. You can do this by adding the following code above the code already given:

Properties properties = System.getProperties();
properties.put("python.path", ".\\src\\test\\resources");  // example of path to custom modules (you change this to where the custom modules you would want to import are)
PythonInterpreter.initialize(System.getProperties(), properties, new String[0]);

You can also return values if you need to go the other way round which can be found in the Jython documentation

Eno Gerguri
  • 639
  • 5
  • 22