68

What's the easiest way to execute a Python script from Java, and receive the output of that script? I've looked for different libraries like Jepp or Jython, but most appear out of date. Another problem with the libraries is that I need to be able to easily include a library with the source code (though I don't need to source for the library itself) if I use a library.

Because of this, would the easiest/most effective way be to simply do something like call the script with runtime.exec, and then somehow capture printed output? Or, even though it would be very painful for me, I could also just have the Python script output to a temporary text file, then read the file in Java.

Note: the actual communication between Java and Python is not a requirement of the problem I am trying to solve. This is, however, the only way I can think of to easily perform what needs to be done.

abhi
  • 1,760
  • 1
  • 24
  • 40
404 Not Found
  • 3,635
  • 2
  • 28
  • 34
  • 1
    I tested the previous answer of John Huang, published here [git hub project jpserve](https://github.com/johnhuang-cn/jpserve) It is the best solution because it links a java client to a real Python application that can execute your scripts, files, ... This is good for super complex projects where you need last updated Python library like Tensorflow, ... The only issue is that you have to do all in one session (not like in an interpreter), so you have to work on preparing your feedback back to java. But this is super easy compared to installing Tensorflow in java python libraries Thank you [Joh – Dorin Feb 02 '17 at 09:13

10 Answers10

79

Not sure if I understand your question correctly, but provided that you can call the Python executable from the console and just want to capture its output in Java, you can use the exec() method in the Java Runtime class.

Process p = Runtime.getRuntime().exec("python yourapp.py");

You can read up on how to actually read the output from this resource: http://www.devdaily.com/java/edu/pj/pj010016 import java.io.*;

public class JavaRunCommand {

    public static void main(String args[]) {

        String s = null;

        try {
            
        // run the Unix "ps -ef" command
            // using the Runtime exec method:
            Process p = Runtime.getRuntime().exec("ps -ef");
            
            BufferedReader stdInput = new BufferedReader(new 
                 InputStreamReader(p.getInputStream()));

            BufferedReader stdError = new BufferedReader(new 
                 InputStreamReader(p.getErrorStream()));

            // read the output from the command
            System.out.println("Here is the standard output of the command:\n");
            while ((s = stdInput.readLine()) != null) {
                System.out.println(s);
            }
            
            // read any errors from the attempted command
            System.out.println("Here is the standard error of the command (if any):\n");
            while ((s = stdError.readLine()) != null) {
                System.out.println(s);
            }
            
            System.exit(0);
        }
        catch (IOException e) {
            System.out.println("exception happened - here's what I know: ");
            e.printStackTrace();
            System.exit(-1);
        }
    }
}

There is also an Apache library (the Apache exec project) that can help you with this. You can read more about it here:

http://www.devdaily.com/java/java-exec-processbuilder-process-1

http://commons.apache.org/exec/

MarkAllen4512
  • 99
  • 1
  • 1
  • 9
Sevas
  • 4,215
  • 3
  • 27
  • 26
  • I have been thinking of making a workflow engine where the workflow functions would be python scripts and the engine it self a java based application. Would you know what would be the performance costs of continuously calling small python scripts one after another? – Saky May 18 '16 at 03:58
  • 3
    Running `Runtime.getRuntime().exec("python yourapp.py");` threw an exception: `java.io.IOException: Cannot run program "python": CreateProcess error=2, The system cannot find the file specified` please note that python is part of my PATH variable – Joey Baruch Feb 23 '17 at 11:31
  • 1
    @joeybaruch I solved that issue by point to the absolute path of my python executable, I.e. `Runtime.getRuntime().exec("C:\\Python27\\python.exe yourapp.py")` – Janac Meena Jan 24 '18 at 20:02
  • 1
    @Janac Meena I tried the absolute path method you have given and it doesn't produce an error.However my python program does not generate an output from the java( eclipse ide).My python program is to simply create a text file into a desktop. – user123 Apr 24 '18 at 14:56
  • @user123 if there's no error, then there might be an issue with your python script. Have you successfully run your script outside of the Java app? – Janac Meena Apr 25 '18 at 19:20
  • @user123 I had the same problem when I was supplying arguments to the command to exec(). Eg `exec(new String[]{"cmd /c python", , })` produced no output. When I concatenated all the args it worked ie `exec("cmd /c python " + + " " + )` – Motorhead Oct 02 '20 at 19:36
  • p.waitFor(), which waits for the python script to finish in this example, should most likely be added after the creation of the process to minimize errors in larger programs. – OrestesK May 23 '22 at 23:54
36

You can include the Jython library in your Java Project. You can download the source code from the Jython project itself.

Jython does offers support for JSR-223 which basically lets you run a Python script from Java.

You can use a ScriptContext to configure where you want to send your output of the execution.

For instance, let's suppose you have the following Python script in a file named numbers.py:

for i in range(1,10):
    print(i)

So, you can run it from Java as follows:

public static void main(String[] args) throws ScriptException, IOException {

    StringWriter writer = new StringWriter(); //ouput will be stored here
    
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptContext context = new SimpleScriptContext();
    
    context.setWriter(writer); //configures output redirection
    ScriptEngine engine = manager.getEngineByName("python");
    engine.eval(new FileReader("numbers.py"), context);
    System.out.println(writer.toString()); 
}

And the output will be:

1
2
3
4
5
6
7
8
9

As long as your Python script is compatible with Python 2.5 you will not have any problems running this with Jython.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
8

I met the same problem before, also read the answers here, but doesn't found any satisfy solution can balance the compatibility, performance and well format output, the Jython can't work with extend C packages and slower than CPython. So finally I decided to invent the wheel myself, it took my 5 nights, I hope it can help you too: jpserve(https://github.com/johnhuang-cn/jpserve).

JPserve provides a simple way to call Python and exchange the result by well format JSON, few performance loss. The following is the sample code.

At first, start jpserve on Python side

>>> from jpserve.jpserve import JPServe
>>> serve = JPServe(("localhost", 8888))
>>> serve.start()

INFO:JPServe:JPServe starting...
INFO:JPServe:JPServe listening in localhost 8888

Then call Python from JAVA side:

PyServeContext.init("localhost", 8888);
PyExecutor executor = PyServeContext.getExecutor();
script = "a = 2\n"
    + "b = 3\n"
    + "_result_ = a * b";

PyResult rs = executor.exec(script);
System.out.println("Result: " + rs.getResult());

---
Result: 6
John Huang
  • 81
  • 1
  • 4
  • You might want to read [How to offer personal open-source libraries?](https://meta.stackexchange.com/q/229085) before posting about your project. – ChrisF Oct 11 '16 at 09:45
  • Thanks for your suggestion. – John Huang Oct 11 '16 at 10:03
  • 1
    I noticed that after execution of a script it does not remember its variables i.e. for example loading of a 500GB file must be done in every call. Is it possible to initialize some variables or keep the variables remembered? – Radim Burget Jun 17 '17 at 16:08
  • 1
    You can set global variable in the script. As I remember, Python can set global variable explicitly. I already forgot the detail syntax since I don't wrote Python script for a long time. – John Huang Jun 19 '17 at 01:58
  • @John Huang, i appreciate your effort on your project. But your solution seems to be working only on UNIX platform.. – Raghu Jun 18 '19 at 17:17
5

Jep is anther option. It embeds CPython in Java through JNI.

import jep.Jep;
//...
    try(Jep jep = new Jep(false)) {
        jep.eval("s = 'hello world'");
        jep.eval("print(s)");
        jep.eval("a = 1 + 2");
        Long a = (Long) jep.getValue("a");
    }
Ariel
  • 327
  • 2
  • 6
  • I think JEP should be the accepted answer. Perhaps this question is rather old so new answers mentioning JEP is underrated compared to older solutions. – Huy Ngo Oct 17 '22 at 09:00
4

I've looked for different libraries like Jepp or Jython, but most seem to be very out of date.

Jython is not "a library"; it's an implementation of the Python language on top of the Java Virtual Machine. It is definitely not out of date; the most recent release was Feb. 24 of this year. It implements Python 2.5, which means you will be missing a couple of more recent features, but it is honestly not much different from 2.7.

Note: the actual communication between Java and Python is not a requirement of the aforementioned assignment, so this isn't doing my homework for me. This is, however, the only way I can think of to easily perform what needs to be done.

This seems extremely unlikely for a school assignment. Please tell us more about what you're really trying to do. Usually, school assignments specify exactly what languages you'll be using for what, and I've never heard of one that involved more than one language at all. If it did, they'd tell you if you needed to set up this kind of communication, and how they intended you to do it.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • You're right on both counts. I actually knew that Jython is still updated, I just didn't really clarify. On the second note, what I'm really trying to do is parse a string using regular expressions, however, the expression I'm using are not supported in Java. Yes, there are ways I could do this using only Java with simpler expressions, but that would mean more logic coding to parse the string, while this method (that is, the current regular expression I'm using) gives me pretty much exactly what I need. – 404 Not Found Apr 11 '12 at 02:10
  • Damn, well, it looks like even Python won't support the expressions I'm trying to use. I guess I'm going to have to rework it after all. – 404 Not Found Apr 11 '12 at 02:24
  • "more logic coding to parse the string" will be **much** less effort over all, no matter what two langauges you want to use together or how you're planning to do it. Trust me. Why don't you ask the question you're really wondering about - i.e. **how to parse the string**? Again, since this is a school assignment, they almost certainly don't mean for you to use advanced regex features. – Karl Knechtel Apr 11 '12 at 04:07
  • Well, the regex I was trying to use was to get matching parenthesis using balancing groups, but apparently it's only supported by .NET languages. In the end I just gave up trying to do nested (since it wasn't required), and just changed it to only single level. – 404 Not Found Apr 11 '12 at 04:42
  • 4
    Balancing brackets is beyond the scope of what can normally sanely be called a regular expression, and is far beyond the scope of what is strictly called a regular expression. In fact, it is pretty much the canonical example of what you **can't** do with regular expressions. – Karl Knechtel Apr 11 '12 at 04:51
3

Jython approach

Java is supposed to be platform independent, and to call a native application (like python) isn't very platform independent.

There is a version of Python (Jython) which is written in Java, which allow us to embed Python in our Java programs. As usually, when you are going to use external libraries, one hurdle is to compile and to run it correctly, therefore we go through the process of building and running a simple Java program with Jython.

We start by getting hold of jython jar file:

https://www.jython.org/download.html

I copied jython-2.5.3.jar to the directory where my Java program was going to be. Then I typed in the following program, which do the same as the previous two; take two numbers, sends them to python, which adds them, then python returns it back to our Java program, where the number is outputted to the screen:

import org.python.util.PythonInterpreter; 
import org.python.core.*; 

class test3{
    public static void main(String a[]){

        PythonInterpreter python = new PythonInterpreter();

        int number1 = 10;
        int number2 = 32;
        python.set("number1", new PyInteger(number1));
        python.set("number2", new PyInteger(number2));
        python.exec("number3 = number1+number2");
        PyObject number3 = python.get("number3");
        System.out.println("val : "+number3.toString());
    }
}

I call this file "test3.java", save it, and do the following to compile it:

javac -classpath jython-2.5.3.jar test3.java

The next step is to try to run it, which I do the following way:

java -classpath jython-2.5.3.jar:. test3

Now, this allows us to use Python from Java, in a platform independent manner. It is kind of slow. Still, it's kind of cool, that it is a Python interpreter written in Java.

Emma
  • 27,428
  • 11
  • 44
  • 69
2

ProcessBuilder is very easy to use.

ProcessBuilder pb = new ProcessBuilder("python","Your python file",""+Command line arguments if any);
Process p = pb.start();

This should call python. Refer to the process approach here for full example!

https://bytes.com/topic/python/insights/949995-three-ways-run-python-programs-java

Palak Bansal
  • 810
  • 12
  • 26
1

You can try using groovy. It runs on the JVM and it comes with great support for running external processes and extracting the output:

http://groovy.codehaus.org/Executing+External+Processes+From+Groovy

You can see in this code taken from the same link how groovy makes it easy to get the status of the process:

println "return code: ${ proc.exitValue()}"
println "stderr: ${proc.err.text}"
println "stdout: ${proc.in.text}" // *out* from the external program is *in* for groovy
txominpelu
  • 1,067
  • 1
  • 6
  • 11
1

First I would recommend to use ProcessBuilder ( since 1.5 )
Simple usage is described here
https://stackoverflow.com/a/14483787
For more complex example refer to
http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html

I've encountered problem when launching Python script from Java, script was producing too much output to standard out and everything went bad.

Community
  • 1
  • 1
partTimeNinja
  • 191
  • 1
  • 12
0

The best way to achieve would be to use Apache Commons Exec as I use it for production without problems even for Java 8 environment because of the fact that it lets you execute any external process (including python, bash etc) in synchronous and asynchronous way by using watchdogs.

 CommandLine cmdLine = new CommandLine("python");
 cmdLine.addArgument("/my/python/script/script.py");
 DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();

 ExecuteWatchdog watchdog = new ExecuteWatchdog(60*1000);
 Executor executor = new DefaultExecutor();
 executor.setExitValue(1);
 executor.setWatchdog(watchdog);
 executor.execute(cmdLine, resultHandler);

 // some time later the result handler callback was invoked so we
 // can safely request the exit value
 resultHandler.waitFor();

Complete source code for a small but complete POC is shared here that addresses another concern in this post;

https://github.com/raohammad/externalprocessfromjava.git

khawarizmi
  • 593
  • 5
  • 19