You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2024/06/17 16:48:41 UTC

(nifi) branch main updated: NIFI-13394 Check candidate directory for python command

This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new d25efb385c NIFI-13394 Check candidate directory for python command
d25efb385c is described below

commit d25efb385c8464e3817ea7f7e8e2edcb9d24a650
Author: Bob Paulin <bo...@bobpaulin.com>
AuthorDate: Wed Jun 12 18:21:33 2024 -0500

    NIFI-13394 Check candidate directory for python command
    
    This closes #8961
    
    Signed-off-by: David Handermann <ex...@apache.org>
---
 .../main/java/org/apache/nifi/py4j/PythonProcess.java | 13 +++++++++++--
 .../java/org/apache/nifi/py4j/PythonProcessTest.java  | 19 +++++++++++++++++--
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/py4j/PythonProcess.java b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/py4j/PythonProcess.java
index cc805467c1..52fe2e8a7f 100644
--- a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/py4j/PythonProcess.java
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/py4j/PythonProcess.java
@@ -17,6 +17,7 @@
 
 package org.apache.nifi.py4j;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.py4j.client.JavaObjectBindings;
 import org.apache.nifi.py4j.client.NiFiPythonGateway;
@@ -302,14 +303,22 @@ public class PythonProcess {
         } else if (virtualEnvDirectories.length == 1) {
             commandExecutableDirectory = virtualEnvDirectories[0].getName();
         } else {
-            // Default to bin directory for macOS and Linux
-            commandExecutableDirectory = "bin";
+            commandExecutableDirectory = findExecutableDirectory(pythonCmd, virtualEnvDirectories);
         }
 
         final File pythonCommandFile = new File(virtualEnvHome, commandExecutableDirectory + File.separator + pythonCmd);
         return pythonCommandFile.getAbsolutePath();
     }
 
+    String findExecutableDirectory(final String pythonCmd, final File[] virtualEnvDirectories) throws IOException {
+        // Check for python command.
+        return List.of(virtualEnvDirectories)
+                .stream()
+                .filter(file -> ArrayUtils.isNotEmpty(file.list((dir, name) -> name.startsWith(pythonCmd))))
+                .findFirst()
+                .orElseThrow(() -> new IOException("Failed to find Python command [%s]".formatted(pythonCmd)))
+                .getName();
+    }
 
     private void setupEnvironment() throws IOException {
         // Environment creation is only necessary if using PIP. Otherwise, the Process requires no outside dependencies, other than those
diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/test/java/org/apache/nifi/py4j/PythonProcessTest.java b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/test/java/org/apache/nifi/py4j/PythonProcessTest.java
index 8797d66eba..8366f248f8 100644
--- a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/test/java/org/apache/nifi/py4j/PythonProcessTest.java
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/test/java/org/apache/nifi/py4j/PythonProcessTest.java
@@ -91,19 +91,34 @@ class PythonProcessTest {
     }
 
     @Test
-    void testResolvePythonCommandPreferBin() throws IOException {
+    void testResolvePythonCommandFindCommand() throws IOException {
         final File binDir = new File(virtualEnvHome, UNIX_BIN_DIR);
         assertTrue(binDir.mkdir());
         final File scriptsDir = new File(virtualEnvHome, WINDOWS_SCRIPTS_DIR);
         assertTrue(scriptsDir.mkdir());
 
+        final File fakeWindowsPythonExe = new File(scriptsDir, PYTHON_CMD + ".exe");
+        assertTrue(fakeWindowsPythonExe.createNewFile());
+
         when(pythonProcessConfig.getPythonCommand()).thenReturn(PYTHON_CMD);
         final String result = this.pythonProcess.resolvePythonCommand();
 
-        final String expected = getExpectedBinaryPath(UNIX_BIN_DIR);
+        final String expected = getExpectedBinaryPath(WINDOWS_SCRIPTS_DIR);
         assertEquals(expected, result);
     }
 
+    @Test
+    void testResolvePythonCommandFindCommandMissingPythonCmd() throws IOException {
+        final File binDir = new File(virtualEnvHome, UNIX_BIN_DIR);
+        assertTrue(binDir.mkdir());
+        final File scriptsDir = new File(virtualEnvHome, WINDOWS_SCRIPTS_DIR);
+        assertTrue(scriptsDir.mkdir());
+
+        when(pythonProcessConfig.getPythonCommand()).thenReturn(PYTHON_CMD);
+
+        assertThrows(IOException.class, () -> this.pythonProcess.resolvePythonCommand());
+    }
+
     @Test
     void testResolvePythonCommandNone() {
         when(pythonProcessConfig.getPythonCommand()).thenReturn(PYTHON_CMD);