diff --git a/java-python-interop/README.md b/java-python-interop/README.md
new file mode 100644
index 0000000000..dc9573ecde
--- /dev/null
+++ b/java-python-interop/README.md
@@ -0,0 +1,5 @@
+## Java Python Interop
+
+This module contains articles about Java and Python interoperability.
+
+### Relevant Articles:
diff --git a/java-python-interop/pom.xml b/java-python-interop/pom.xml
new file mode 100644
index 0000000000..6ee5a0be3b
--- /dev/null
+++ b/java-python-interop/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+ java-python-interop
+ 0.0.1-SNAPSHOT
+ java-python-interop
+
+
+ com.baeldung
+ parent-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.python
+ jython-slim
+ ${jython.version}
+
+
+ org.apache.commons
+ commons-exec
+ ${commons-exec.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+
+
+ java-python-interop
+
+
+ src/main/resources
+ true
+
+
+ src/test/resources
+ true
+
+
+
+
+
+ 2.7.2
+ 1.3
+ 3.6.1
+
+
+
\ No newline at end of file
diff --git a/java-python-interop/src/main/java/com/baeldung/python/interop/ScriptEngineManagerUtils.java b/java-python-interop/src/main/java/com/baeldung/python/interop/ScriptEngineManagerUtils.java
new file mode 100644
index 0000000000..981f174c33
--- /dev/null
+++ b/java-python-interop/src/main/java/com/baeldung/python/interop/ScriptEngineManagerUtils.java
@@ -0,0 +1,34 @@
+package com.baeldung.python.interop;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+
+public class ScriptEngineManagerUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ScriptEngineManagerUtils.class);
+
+ private ScriptEngineManagerUtils() {
+ }
+
+ public static void listEngines() {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ List 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);
+ }
+ }
+ }
+
+}
diff --git a/java-python-interop/src/main/resources/logback.xml b/java-python-interop/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/java-python-interop/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java-python-interop/src/test/java/com/baeldung/python/interop/JavaPythonInteropUnitTest.java b/java-python-interop/src/test/java/com/baeldung/python/interop/JavaPythonInteropUnitTest.java
new file mode 100644
index 0000000000..5ec3a2b61f
--- /dev/null
+++ b/java-python-interop/src/test/java/com/baeldung/python/interop/JavaPythonInteropUnitTest.java
@@ -0,0 +1,131 @@
+package com.baeldung.python.interop;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleScriptContext;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
+import org.apache.commons.exec.PumpStreamHandler;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.python.core.PyException;
+import org.python.core.PyObject;
+import org.python.util.PythonInterpreter;
+
+public class JavaPythonInteropUnitTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void givenPythonScript_whenPythonProcessInvoked_thenSuccess() throws Exception {
+ ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello.py"));
+ processBuilder.redirectErrorStream(true);
+
+ Process process = processBuilder.start();
+ List 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 Baeldung Readers!!")));
+
+ int exitCode = process.waitFor();
+ assertEquals("No errors should be detected", 0, exitCode);
+ }
+
+ @Test
+ public void givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed() throws Exception {
+ StringWriter output = new StringWriter();
+ ScriptContext context = new SimpleScriptContext();
+ context.setWriter(output);
+
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByName("python");
+ engine.eval(new FileReader(resolvePythonScriptPath("hello.py")), context);
+ assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", output.toString()
+ .trim());
+ }
+
+ @Test
+ public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() {
+ try (PythonInterpreter pyInterp = new PythonInterpreter()) {
+ StringWriter output = new StringWriter();
+ pyInterp.setOut(output);
+
+ pyInterp.exec("print('Hello Baeldung Readers!!')");
+ assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", output.toString()
+ .trim());
+ }
+ }
+
+ @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());
+ }
+ }
+
+ @Test
+ public void givenPythonInterpreter_whenErrorOccurs_thenExceptionIsThrown() {
+ thrown.expect(PyException.class);
+ thrown.expectMessage("ImportError: No module named syds");
+
+ try (PythonInterpreter pyInterp = new PythonInterpreter()) {
+ pyInterp.exec("import syds");
+ }
+ }
+
+ @Test
+ public void givenPythonScript_whenPythonProcessExecuted_thenSuccess() throws ExecuteException, 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);
+ assertEquals("No errors should be detected", 0, exitCode);
+ assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", outputStream.toString()
+ .trim());
+ }
+
+ private List 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();
+ }
+
+}
diff --git a/java-python-interop/src/test/resources/hello.py b/java-python-interop/src/test/resources/hello.py
new file mode 100644
index 0000000000..13275d9257
--- /dev/null
+++ b/java-python-interop/src/test/resources/hello.py
@@ -0,0 +1 @@
+print("Hello Baeldung Readers!!")
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index c6addfa6aa..1a56184de2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -461,7 +461,8 @@
java-lite
java-numbers
java-numbers-2
- java-numbers-3
+ java-numbers-3
+ java-python-interop
java-rmi
java-spi
java-vavr-stream