Python is an increasingly popular programming language, especially in the scientific community, because of its rich digital and statistical packages. Therefore, the need to be able to call Python code from Java applications is not uncommon.

In this tutorial, we’ll look at some of the most common ways to call Python code from Java.

Runtime environment

Java version "1.8.0 comes with _191" Python 3.8.3Copy the code

The preparatory work

A very simple Python script named hello.py

print("hello python")
Copy the code

Running the script has the following effect:

$ python hello.pyhello pythonCopy the code

Hello.py is placed in the Resources directory of your Java project.

Actual part

Use the ProcessBuilder

public class ProcessBuilderDemo {    public static void main(String[] args) throws IOException {        ProcessBuilder processBuilder = new ProcessBuilder("python",                resolvePythonScriptPath("hello.py"));        processBuilder.redirectErrorStream(true);              Process process = processBuilder.start();        List<String> results = readProcessOutput(process.getInputStream());        System.out.println("results = " + results);    }    private static String resolvePythonScriptPath(String filename) {        File file = new File("src/main/resources/" + filename);        return file.getAbsolutePath();    }    private static List<String> readProcessOutput(InputStream inputStream) throws IOException {        try (BufferedReader output = new BufferedReader(new InputStreamReader(inputStream))) {            return output.lines()                    .collect(Collectors.toList());        }    }}
Copy the code

One thing to note is the property redirectErrorStream(True), if anything goes wrong the error output will be merged with standard output. If we did not set this property to true then we would need to read output from two separate streams using the getInputStream() and getErrorStream() methods.

! [](https://static001.geekbang.org/infoq/94/941e58641c6ea1eb97ba45e54270c1f8.png)

Program execution result

Let’s modify the following code to see the output in action:

ProcessBuilder ProcessBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello2.py")); processBuilder.redirectErrorStream(true);Copy the code

Result If the execution fails, an error message is displayed:

! [](https://static001.geekbang.org/infoq/7f/7f543e12842df0c04e9cf0e9eb4ffe7a.png)

Leaving the ReDirecStream (True) line out and running the console won’t print what we expect and error messages won’t print.

ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello2.py")); //processBuilder.redirectErrorStream(true);Copy the code

! [](https://static001.geekbang.org/infoq/e9/e9f919f86117df220a68aea79909c399.png)

In this case, to get the error message, modify the following code:

public static void main(String[] args) throws IOException { ProcessBuilder processBuilder = new ProcessBuilder("python",  resolvePythonScriptPath("hello2.py")); // processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); List<String> results = readProcessOutput(process.getInputStream()); List<String> errMsg = readProcessOutput(process.getErrorStream()); System.out.println("results = " + results); System.out.println("errMsg = " + errMsg); }Copy the code

! [](https://static001.geekbang.org/infoq/8c/8c1316d0a27401cc6ab95af4a6348d3e.png)

Isn’t it tedious to write something like this so simply set It to ReDirecStream (True) and the world is clear.

Using it

Jython’s official website: www.jython.org

Jsr-223 Script engine

Jsr-223, first introduced in Java 6, defines a set of scripting apis that provide basic scripting capabilities. These methods provide mechanisms for executing scripts and sharing values between Java and the scripting language. The main purpose of the standard is to try to make Java interoperable with different scripting languages.

Of course, we can use the pluggable script engine architecture for any dynamic language that has a JVM implementation. Jython is a Java platform implementation of Python that runs on the JVM.

Introduce dependencies in POM.xml

< the dependency > < groupId > org. Python < / groupId > < artifactId > jython < / artifactId > < version > 2.7.2 < / version > < / dependency >Copy the code

Use the following code to list all available scripting engines:

public static void listEngines() { ScriptEngineManager manager = new ScriptEngineManager(); List<ScriptEngineFactory> engines = manager.getEngineFactories(); for (ScriptEngineFactory engine : engines) { System.out.println("Engine name:" + engine.getEngineName()); System.out.println("Version: " + engine.getEngineVersion()); System.out.println("Language: " + engine.getLanguageName()); System.out.println("Short Names:"); for (String names : engine.getNames()) { System.out.println(names); }}}Copy the code

We can use Jython if we see the console printing the corresponding script engine below

! [](https://static001.geekbang.org/infoq/69/6950fba1598c9133fc85ca35070951e3.png)

The code logic using the Jython scripting engine is as follows:

public static void main(String[] args) throws IOException, ScriptException { StringWriter writer = new StringWriter(); ScriptContext context = new SimpleScriptContext(); context.setWriter(writer); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("python"); engine.eval(new FileReader(resolvePythonScriptPath("hello.py")), context); System.out.println(" result: "+ writer.tostring ()); }Copy the code

We use the ScriptEngineManager class’s getEngineByName method to find and create a ScriptEngine for a given short name. In our case, we can pass Python or Jython, the two short names associated with the engine. ,

! [](https://static001.geekbang.org/infoq/1c/1c6cd3a92e0c03c1c913f7f0cc206c37.png)

The execution result

Manager. GetEngineByName (“python”);

1. Set options. importSite = false before calling getEngineByName

The simplified code is as follows:

import org.python.core.Options; ScriptEngineManager manager = new ScriptEngineManager(); Options.importSite = false; ScriptEngine engine = manager.getEngineByName("python");Copy the code

2. Use separate jars provided by the authorities

<! --<dependency> <groupId>org.python</groupId> <artifactId>jython</artifactId> < version > 2.7.2 < / version > < / dependency > -- > < the dependency > < groupId > org. Python < / groupId > < artifactId > jython - standalone < / artifactId > < version > 2.7.2 < / version > < / dependency >Copy the code

! [](https://static001.geekbang.org/infoq/8f/8f13a28739ecff39c9db4aabc538a859.png)

Execute successfully

Python code is embedded directly into Java code

Using the PythonInterpretor class, you can embed Python code directly into Java code.

public static void main(String[] args) { try (PythonInterpreter pyInterp = new PythonInterpreter()) { StringWriter output = new StringWriter(); pyInterp.setOut(output); pyInterp.exec("print('Hello jython')"); System.out.println(" output: "+ output.tostring ()); }}Copy the code

! [](https://static001.geekbang.org/infoq/80/8013f4dd5bab3f833b5827685cc45177.png)

Another mistake…

There are two solutions:

Set python.import.site=false

public static void main(String[] args) { Properties props = new Properties(); props.put("python.import.site", "false"); PythonInterpreter.initialize(System.getProperties(), props, null); try (PythonInterpreter pyInterp = new PythonInterpreter()) { StringWriter output = new StringWriter(); pyInterp.setOut(output); pyInterp.exec("print('Hello jython')"); System.out.println(" output: "+ output.tostring ()); }}Copy the code

2. Use separate jars provided by the authorities

<! --<dependency> <groupId>org.python</groupId> <artifactId>jython</artifactId> < version > 2.7.2 < / version > < / dependency > -- > < the dependency > < groupId > org. Python < / groupId > < artifactId > jython - standalone < / artifactId > < version > 2.7.2 < / version > < / dependency >Copy the code

! [](https://static001.geekbang.org/infoq/19/19964a99def89f5c5ab5319c0537a41f.png)

The successful running

Take another example of adding two numbers together, using the _get_ method to access the value of a variable.

public static void main(String[] args) { Properties props = new Properties(); props.put("python.import.site", "false"); PythonInterpreter.initialize(System.getProperties(), props, null); try (PythonInterpreter pyInterp = new PythonInterpreter()) { pyInterp.exec("x = 10 + 10"); PyObject x = pyInterp.get("x"); System.out.println(" execute result: "+ x.assint ()); }}Copy the code

! [](https://static001.geekbang.org/infoq/54/54e746419baaa8f7423637176365baad.png)

The results

Apache Commons Exec

Another third-party library we can use is Apache Common Exec, which attempts to overcome some of the shortcomings of the Java Process API.

Introduce dependencies in POM.xml

<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-exec</artifactId> The < version > 1.3 < / version > < / dependency >Copy the code

public static void main(String[] args) throws IOException { String line = "python " + resolvePythonScriptPath("hello.py"); CommandLine cmdLine = CommandLine.parse(line); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); DefaultExecutor executor = new DefaultExecutor(); executor.setStreamHandler(streamHandler); int exitCode = executor.execute(cmdLine); System.out.println(" execute status, success = 0, failure = "+ exitCode); System.out.println(" result: "+ outputStream.toString()); }Copy the code

! [](https://static001.geekbang.org/infoq/1a/1a1381faffe97f14b6e841f9d35b915c.png)

The execution result

Use HTTP for operations

To recap, instead of trying to call Python directly, consider using a full-fledged protocol (such as HTTP) as an abstraction layer between two different languages.

In fact, Python comes with a simple built-in HTTP server that we can use to share content or files over HTTP:

python -m http.server 9000
Copy the code

If we now go to http://localhost:9000, we will see the contents of the directory where the previous command was launched.

There are other popular frameworks to consider for creating more robust Python-based Web services or applications: Flask and Django.

Once we have an endpoint we can access, we can invoke our Python Web service/application implementation using any of several Java HTTP libraries.