-
Notifications
You must be signed in to change notification settings - Fork 38
Enh/ap 25245 pixi env creator #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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() { |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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() { |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deprecation message should explain what to use instead. Add 'Use {@link #hasEnvironmentPort()} instead' to the @deprecated tag.
| @Deprecated | ||
| @Override | ||
| ProcessBuilder createProcessBuilder(); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should drop the pixi port here, right?
…uld be removed completely? Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment is outdated
| * @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()); | ||
| } |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()); | ||
| } |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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(); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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