Java calls Python methods

At present, the system has received a requirement that the platform can support running Python scripts, and our platform is written in Java. Therefore, we need to predict our final implementation plan.

After research, there are currently several ways for Java to call Python scripts.

1. Using jython

Jython is a JVM implementation of the Python programming language It is designed to run on the Java platform Jython programs can import and use any Java class Just like Java, Jython programs are compiled as bytecode One of the main advantages is that user interfaces designed in Python can use AWT, Swing, or SWT Package GUI elements

<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.0</version>
</dependency>
Map<String, Object> binding = new HashMap<>();
binding.put("name", "zhangshan");
binding.put("age", "18");
try (PythonInterpreter interpreter = new PythonInterpreter(Py.java2py(binding))) {
interpreter.exec("varMap = globals()");
interpreter.exec("varMap.put('name', 'ls')");
interpreter.exec("varMap.put('sex', '1')");
PyObject vars = interpreter.get("varMap");
System.out.println(vars.toString());
}

The benefits of this approach are: .

  • No dependence on Python environment
  • Can easily bind parameters, suitable for parameter interaction between Java programs and Python scripts
  • No need to generate scripts for file management, simply execute script strings

The disadvantages are also very obvious: .

  • Up to version 2.7 of Python is only supported
  • Some dependency packages written in C language are not supported

2. Using command line mode

The Java. lang. runtime. exec (String command, String [] envp, File dir) method executes the specified string command in a separate process in the specified environment and working directory. This is a convenient method. The calling behavior of exec (command, envp, dir) is exactly the same as calling exec (cmdarray, envp, dir), where cmdarray is an array of all tags in the command

int a = 18;
int b = 23;
try {
String[] args1 = new String[] { "python", "/Users/rayduan/PycharmProjects/pythonProject/test.py", String.valueOf(a), String.valueOf(b) };
Process proc = Runtime.getRuntime().exec(args1);//implement py file 
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
  System.out.println(line);
}
in.close();
proc.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

The benefits of this approach are obvious: .

  • Can support various versions of Python
  • Support various forms of third-party packages

But there are also many drawbacks: .

  • Unable to exchange parameters with Java program
  • To obtain the return result, you need to use print to output the return result and change the way the user uses the function
if __name__== '__main__':
print(sys.path)
  • Script needs to be maintained as a py file for management
  • Compatibility of commands on different systems
  • The command parameter of Runtime.axec() is just a runnable command or script, and is not equivalent to a Shell solver or Cmdexe. If you want to perform input-output redirection, pipeline, and other operations, it must be implemented through a program. It cannot be done directly in the command parameter
  • Runtime.axec() may hang or even deadlock (The child processes created by Runtime.axec() share the stream of the parent process, and on different platforms, the stream buffer of the parent process may be filled, causing the child process to block and never be able to return.)

3. Python calls Python scripts directly (by exposing interfaces)

#!/usr/local/bin/python3.7
import time
import os 
count = 0
str = ('python b.py')
result1 = os.system(str)
print(result1)
while True:
count = count + 1
if count == 8:
print('this count is:',count) 
break
else:
time.sleep(1)
print('this count is:',count)   
print('Good Bye')

The os. system () method executes a command (string) in a subshell. This method is implemented by calling the standard C function system () and has the same restrictions. If the command generates any output, it is sent to the interpreter's standard output stream. Whenever this method is used, the corresponding shell of the operating system is opened and the command is executed on it. Unfortunately, it does not return a value

4. Use Jep

Jep uses JNI and CPython APIs to start the Python interpreter in the JVM. When you create an Interpreter instance in Java, a Python interpreter will be created for that Java Interpreter instance and kept in memory until the Interpreter instance is closed using Interpreter. close(). Due to the need to manage consistent Python thread state, the thread that creates the Interpreter instance must be reused in all method calls to that Interpreter instance Jep should work with any pure Python module. It can work with various CPython extensions. Developers have reported that Jep can work with NumPy, Scipy, Pandas, TensorFlow, Matplotlib, cvxpy, and more

  • After installing the Python runtime environment, we will install Jep
pip3 install jep
  • Configure the database name path for jep export LD_LIBRARY_PATH= "< your user path>/myenv/lib/python3.8/site packages/jet" $LD_LIBRARY_PATH (If not effective, specify library address - DJava. library. path=/Library/Frameworks/Python. framework/Version/3.8/lib/Python 3.8/site packages/jet/)
  • Introducing the JAR package of Jep into Java programs
<dependency>
<groupId>black.ninia</groupId>
<artifactId>jep</artifactId>
<version>4.0.3</version>
</dependency

After the above configuration, we have set up the running environment for Jep and it is also very easy to use.

/**
 *from python obtaining variable values from 
* @param scriptContent
*/
public void runScriptWithReturn() {
try (SharedInterpreter interpreter = new SharedInterpreter()) {
  interpreter.exec("import sys");
  Object value = interpreter.getValue("sys.path");
  System.out.println(value);
}
}
/**
 *call python methods in the file 
*/
public void runScriptWithFileMethod() {
JepConfig jepConfig = new JepConfig().addIncludePaths("/Users/rayduan/PycharmProjects/pythonProject");
SharedInterpreter.setConfig(jepConfig);
try (Interpreter interpreter = new SharedInterpreter()) {
  interpreter.exec("from demo import *");
  Object result = interpreter.invoke("func", 1, 2);
  System.out.println(result);
}
}
/**
 *by python script modification of values in variables 
*/
public void runScriptWithChangeParam() {
try (Interpreter interpreter = new SharedInterpreter()) {
  Map<String, Object> binding = new HashMap<>();
  binding.put("name", "zhangshan");
  binding.put("age", "18");
  interpreter.set("vars", binding);
  interpreter.exec("vars['name'] = 'ls'");
  Map vars = interpreter.getValue("vars", Map.class);
  System.out.println(vars);
}
}

According to our requirements, Jep can fully cover our usage scenarios. Below, we will discuss the advantages and disadvantages of Jep
Advantages: .

  • No need to generate scripts for file management, simply execute script strings
  • Can support various versions of Python
  • Support various forms of third-party packages
  • Easy to use and able to interact well with Java programs

Disadvantages: .

  • Building a complex environment

Summary: The current requirement is to support Python 3.0, and our platform's scripting function mainly changes input and output parameters or scene runtime parameters through scripts. Based on the above viewpoints, Jep is undoubtedly the best choice.