#The Python language has a wealth of system management, data processing, and statistical software packages, so the need to call Python code from Java applications is very common and practical. DataX is an offline synchronization tool for heterogeneous data sources open sourced by Alibaba. It is dedicated to achieving stable and efficient data between various heterogeneous data sources including relational databases (MySQL, Oracle, etc.), HDFS, Hive, ODPS, HBase, FTP, etc. Sync function. Datax also calls Python scripts through Java.
Java provides two methods, namely ProcessBuilder API and JSR-223 Scripting Engine.
Create a local operating system process through ProcessBuilder to start python and execute the Python script. The hello.py script simply outputs "Hello Python!". The development environment needs to have python installed and environment variables set.
@Test public void givenPythonScript_whenPythonProcessInvoked_thenSuccess() throws Exception { ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello.py")); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); List<String> results = readProcessOutput(process.getInputStream()); assertThat("Results should not be empty", results, is(not(empty()))); assertThat("Results should contain output of script: ", results, hasItem(containsString("Hello Python!"))); int exitCode = process.waitFor(); assertEquals("No errors should be detected", 0, exitCode); } private List<String> readProcessOutput(InputStream inputStream) throws IOException { try (BufferedReader output = new BufferedReader(new InputStreamReader(inputStream))) { return output.lines() .collect(Collectors.toList()); } } private String resolvePythonScriptPath(String filename) { File file = new File("src/test/resources/" + filename); return file.getAbsolutePath(); }
To rewrite this sentence, you can say: Start using the Python command with one argument, which is the full path to the Python script. It can be placed in the resources directory of the java project. What needs to be noted is: redirectErrorStream(true), in order to make the error output stream be merged into the standard output stream when an error occurs when executing the script. Error information can be read by calling the getInputStream() method of the Process object. Without this setting, you need to use two methods to obtain the stream: getInputStream() and getErrorStream(). After getting the Process object from ProcessBuilder, verify the results by reading the output stream.
The JSR-223 specification was first introduced in Java 6. The specification defines a set of scripting APIs that can provide basic scripting functionality. These APIs provide mechanisms for sharing values and executing scripts between Java and scripting languages. The main purpose of this specification is to unify the interaction between Java and dynamic scripting languages that implement different JVMs. Jython is a Java implementation of Python that runs on the JVM. Assuming we have Jython on the CLASSPATH, the framework automatically discovers that we have the possibility to use that scripting engine and allows us to request the Python scripting engine directly. In Maven, we can reference Jython, or download and install it directly
<dependency> <groupId>org.python</groupId> <artifactId>jython</artifactId> <version>2.7.2</version> </dependency>
You can list all supported script engines through the following code:
public static void listEngines() { ScriptEngineManager manager = new ScriptEngineManager(); List<ScriptEngineFactory> engines = manager.getEngineFactories(); for (ScriptEngineFactory engine : engines) { LOGGER.info("Engine name: {}", engine.getEngineName()); LOGGER.info("Version: {}", engine.getEngineVersion()); LOGGER.info("Language: {}", engine.getLanguageName()); LOGGER.info("Short Names:"); for (String names : engine.getNames()) { LOGGER.info(names); } } }
If Jython is available in the environment, you should see To the corresponding output:
...
Engine name: jython
Version: 2.7.2
Language: python
Short Names:
python
jython
Now use Jython to call the hello.py script:
@Test public void givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed() throws Exception { 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); assertEquals("Should contain script output: ", "Hello Python!", writer.toString().trim()); }
Using this API is more concise than the above example. When setting the ScriptContext, you need to include a StringWriter in order to save the output of the script execution. Then provide a short name for ScriptEngineManager to find the script engine, which can use python or jython. Finally, verify that the output is consistent with expectations.
In fact, you can also use the PythonInterpretor class to directly call the embedded python code:
@Test public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() { try (PythonInterpreter pyInterp = new PythonInterpreter()) { StringWriter output = new StringWriter(); pyInterp.setOut(output); pyInterp.exec("print('Hello Python!')"); assertEquals("Should contain script output: ", "Hello Python!", output.toString().trim()); } }
The exec method provided by the PythonInterpreter class can directly execute Python code. Capture the execution output via a StringWriter as in the previous example. Let’s look at another example:
@Test public void givenPythonInterpreter_whenNumbersAdded_thenOutputDisplayed() { try (PythonInterpreter pyInterp = new PythonInterpreter()) { pyInterp.exec("x = 10+10"); PyObject x = pyInterp.get("x"); assertEquals("x: ", 20, x.asInt()); } }
The above example can use the get method to access the variable value. The following example shows how to catch errors:
try (PythonInterpreter pyInterp = new PythonInterpreter()) { pyInterp.exec("import syds"); }
Running the above code will throw a PyException exception, which is the same as executing the Python script locally to output an error.
Here are a few things to note:
PythonIntepreter implements AutoCloseable and is best used with try-with-resources.
The PythonIntepreter class name is not a parser representing Python code. Python programs run in Jython in jvm and need to be compiled into java bytecode before execution.
Although Jython is a Python implementation of Java, it may not contain all the same sub-packages as native Python.
The following example shows how to assign java variables to Python variables:
import org.python.util.PythonInterpreter; import org.python.core.*; class test3{ public static void main(String a[]){ int number1 = 10; int number2 = 32; try (PythonInterpreter pyInterp = new PythonInterpreter()) { 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()); } } }
The above is the detailed content of How to call Python in Java. For more information, please follow other related articles on the PHP Chinese website!