Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds support for a new Python environment provider node with a Pixi port object, integrating it into the Python scripting nodes ecosystem. The changes enable Python script nodes to receive Python environments through an optional input port instead of only relying on configured preferences.
Changes:
- Introduced
PythonProcessProvideras a replacement interface for the deprecatedPythonCommandto support multiple environment sources - Added optional Python environment port support to Python Script and Python View nodes
- Implemented environment port extraction and installation logic with progress reporting
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| org.knime.python3/src/main/java/org/knime/python3/PythonCommand.java | Deprecated PythonCommand interface, now extends PythonProcessProvider for backward compatibility |
| org.knime.python3/src/main/java/org/knime/python3/SimplePythonCommand.java | Updated documentation to reference PythonProcessProvider |
| org.knime.python3/src/main/java/org/knime/python3/QueuedPythonGatewayFactory.java | Refactored to use PythonProcessProvider instead of PythonCommand |
| org.knime.python3/src/main/java/org/knime/python3/PythonGatewayFactory.java | Updated gateway description to work with PythonProcessProvider |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/script/PythonScriptNodeFactory.java | Added optional Python environment port to Python Script node |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/view/PythonViewNodeFactory.java | Added optional Python environment port to Python View node |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonScriptNodeModel.java | Implemented environment port extraction and prioritization over configured Python command |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonScriptingService.java | Added environment port handling for interactive sessions |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonScriptingSession.java | Added PythonCommandAdapter to bridge PythonProcessProvider to legacy PythonCommand |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes/PortsConfigurationUtils.java | Added utility methods for environment port detection and installation |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonIOUtils.java | Updated to filter out environment ports from data processing |
| org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonScriptingInputOutputModelUtils.java | Added logic to skip environment ports in input/output model generation |
...pting.nodes/src/main/java/org/knime/python3/scripting/nodes2/view/PythonViewNodeFactory.java
Outdated
Show resolved
Hide resolved
...g.nodes/src/main/java/org/knime/python3/scripting/nodes2/script/PythonScriptNodeFactory.java
Outdated
Show resolved
Hide resolved
...ipting.nodes/src/main/java/org/knime/python3/scripting/nodes/view/PythonViewNodeFactory.java
Outdated
Show resolved
Hide resolved
...ng.nodes/src/main/java/org/knime/python3/scripting/nodes/script/PythonScriptNodeFactory.java
Outdated
Show resolved
Hide resolved
...scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/PythonScriptingService.java
Outdated
Show resolved
Hide resolved
| @Deprecated(since = "5.10", forRemoval = true) | ||
| public boolean hasPixiPort() { |
There was a problem hiding this comment.
The deprecation message should explain what to use instead. Add 'Use {@link #hasEnvironmentPort()} instead' to the @deprecated tag.
There was a problem hiding this comment.
wait... you introduce this method, which is deprecated, and introduce the replacement at the same time?? How about getting rid of hasPixiPort?
| @Deprecated(since = "5.10", forRemoval = true) | ||
| public boolean hasPythonEnvPort() { |
There was a problem hiding this comment.
The deprecation message should explain what to use instead. Add 'Use {@link #hasEnvironmentPort()} instead' to the @deprecated tag.
| @Deprecated | ||
| @Override | ||
| ProcessBuilder createProcessBuilder(); |
There was a problem hiding this comment.
The @deprecated annotation on interface methods should include forRemoval=true and since parameters for consistency with the interface-level deprecation at line 67.
| protected PortObjectSpec[] configure(final PortObjectSpec[] inSpecs) throws InvalidSettingsException { | ||
| for (int i = 0; i < m_inPorts.length; i++) { | ||
| // The Pixi port (if present) is at the end of the input specs | ||
| final int numRegularPorts = m_inPorts.length; |
There was a problem hiding this comment.
I think we should drop the pixi port here, right?
…uld be removed completely? Co-authored-by: Copilot <[email protected]>
| try { | ||
| // Check if any input port is a PythonEnvironmentPortObject | ||
| for (final PortType inType : inTypes) { | ||
| if (inType.equals(PythonEnvironmentPortObject.TYPE) || inType.equals(PythonEnvironmentPortObject.TYPE_OPTIONAL)) { |
There was a problem hiding this comment.
you could simply use the isPixiPort method from below here, then you don't need to write the check twice
| * @throws IOException if installation fails | ||
| * @throws CanceledExecutionException if the operation is canceled | ||
| */ | ||
| public static void installPythonEnvironmentIfPresent( |
There was a problem hiding this comment.
I'd have expected this method inside the port object, so that the Python node only calls PythonEnvironmentPortObject.installEnvironment(exec) and that does its thing...
| LOGGER.debugWithFormat("Checking if port type '%s' is environment port: %s", | ||
| portType.getName(), isPythonEnvPort); | ||
| return isPythonEnvPort; | ||
| } catch (NoClassDefFoundError e) { |
There was a problem hiding this comment.
you have this check in many places but... don't we have a dependency in the MANIFEST.MF to the package defining the PythonEnvironmentPortObject? How should that ever be missing then?
| if (pythonCommand != null) { | ||
| LOGGER.info("Using Python environment from connected Pixi port for interactive session"); | ||
| // Filter out Pixi port from data ports - it's not a data port for setupIO | ||
| dataPortObjects = java.util.Arrays.copyOf(inputData, inputData.length - 1); |
There was a problem hiding this comment.
import Arrays instead of using the full type here
| if (portObject instanceof PythonEnvironmentPortObject) { | ||
| final PythonEnvironmentPortObject pythonEnvPort = (PythonEnvironmentPortObject)portObject; | ||
| try { | ||
| // PythonEnvironmentPortObject.getPythonCommand() returns org.knime.pixi.port.PythonCommand, |
| * @return the Python command, or null if the port is not connected or doesn't contain a valid Python executable | ||
| * @throws InvalidSettingsException if the Python executable path from the environment doesn't exist | ||
| */ | ||
| private static PythonProcessProvider extractPythonCommandFromPixiPort(final PortObject portObject) |
There was a problem hiding this comment.
again: this is a copy, and it shouldn't live here but in the PortObject
| // Count the number of the different ports (skip the flow var port) | ||
| var numInTables = 0; | ||
| var numInObjects = 0; | ||
| var hasEnvironmentPort = false; |
There was a problem hiding this comment.
aha, see, here it's called hasEnvironmentPort :)
| } else { | ||
| // Check if it's a Python environment port | ||
| try { | ||
| if (PythonEnvironmentPortObject.TYPE.equals(portType) |
There was a problem hiding this comment.
this check already exists somewhere else. Maybe even put the check for isPythonEnvironmentPortObject into the port object itself?
| @Deprecated(since = "5.10", forRemoval = true) | ||
| public boolean hasPixiPort() { |
There was a problem hiding this comment.
wait... you introduce this method, which is deprecated, and introduce the replacement at the same time?? How about getting rid of hasPixiPort?
| Eclipse-RegisterBuddy: org.knime.ext.py4j | ||
| Eclipse-BundleShape: dir | ||
| Bundle-Activator: org.knime.python3.nodes.Activator | ||
| Import-Package: org.knime.python3.processprovider |
There was a problem hiding this comment.
why do we do it like this, and don't add a dependency to the plugin org.knime.python3.processprovider? Then we can also get rid of all the NoClassDefFound checks
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 41 out of 41 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/script/PythonScriptNodeFactory.java:1
- The variable
pythonEnvClassis declared but never used on line 130 and 139. This appears to be a leftover from development and should be removed to reduce code clutter.
/*
org.knime.python3.scripting.nodes/src/main/java/org/knime/python3/scripting/nodes2/view/PythonViewNodeFactory.java:1
- The variable
pythonEnvClassis declared on line 91 in the view factory but never used. This appears to be a leftover from development and should be removed to reduce code clutter.
/*
| try { | ||
| final Class<?> pythonEnvClass = PythonEnvironmentPortObject.class; | ||
| b.addOptionalInputPortGroup(PORTGR_ID_PYTHON_ENV, PythonEnvironmentPortObject.TYPE_OPTIONAL); | ||
| LOGGER.debug("Successfully added Python environment port"); | ||
| } catch (NoClassDefFoundError e) { | ||
| LOGGER.debug("PythonEnvironmentPortObject not available: " + e.getMessage()); | ||
| } |
There was a problem hiding this comment.
The variable pythonEnvClass is declared but never used. This appears to be a leftover from development and should be removed to reduce code clutter.
| try { | ||
| final Class<?> pythonEnvClass = PythonEnvironmentPortObject.class; | ||
| b.addOptionalInputPortGroup(PORTGR_ID_PYTHON_ENV, PythonEnvironmentPortObject.TYPE_OPTIONAL); | ||
| LOGGER.debug("Successfully added Python environment port"); | ||
| } catch (NoClassDefFoundError e) { | ||
| LOGGER.debug("PythonEnvironmentPortObject not available: " + e.getMessage()); | ||
| } |
There was a problem hiding this comment.
The variable pythonEnvClass is declared but never used. This appears to be a leftover from development and should be removed to reduce code clutter.
| */ | ||
| public static BundledPythonCommand getBundledPythonCommand() { | ||
| return getBundledCondaEnvironmentConfig().getPythonCommand(); | ||
| return (BundledPythonCommand)getBundledCondaEnvironmentConfig().getPythonCommand(); |
There was a problem hiding this comment.
This unchecked cast from PythonProcessProvider to BundledPythonCommand is unsafe and could throw ClassCastException at runtime. Consider adding type validation or changing the method signature of getBundledCondaEnvironmentConfig().getPythonCommand() to return the specific type.
| return (BundledPythonCommand)getBundledCondaEnvironmentConfig().getPythonCommand(); | |
| final var pythonCommand = getBundledCondaEnvironmentConfig().getPythonCommand(); | |
| if (!(pythonCommand instanceof BundledPythonCommand)) { | |
| throw new IllegalStateException( | |
| "Bundled Python environment '" + BUNDLED_PYTHON_ENV_ID | |
| + "' does not provide a BundledPythonCommand (got: " | |
| + (pythonCommand == null ? "null" : pythonCommand.getClass().getName()) + ")"); | |
| } | |
| return (BundledPythonCommand)pythonCommand; |
Add support for the new python environment provider node and it's pixi port object to the python scripts