Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ce416a1
AP-25245: add pixi port to python scripting node
marc-lehner Jan 6, 2026
ecb8f80
AP-25245: move ports to dedicated plugin
marc-lehner Jan 7, 2026
566f3c8
AP-25245: use pixi run in pixiPythonCommand for all pixi environment …
marc-lehner Jan 13, 2026
9682bbf
AP-25245: add pixi port to python view
marc-lehner Jan 14, 2026
65cbd5e
AP-25245: add pixi port to interactive execution
marc-lehner Jan 14, 2026
5644fcb
AP-25245: replace pixi port with python port
marc-lehner Jan 19, 2026
42072d1
AP-25245: add progress for the python port installation and make it c…
marc-lehner Jan 21, 2026
a840697
AP-25245: deprecate PythonCommand and use PythonProcessProvider from …
marc-lehner Jan 26, 2026
dce5538
AP-25245: only use import PixiPythonCommand from knime-conda (import …
marc-lehner Jan 26, 2026
c8b6f16
AP-25245: use debug instead of info log for trivial messages.
chaubold Jan 26, 2026
0691a21
AP-25245: update all plugins to 5.11
marc-lehner Feb 10, 2026
54aa7b5
AP-25245: move pixi code to python port
marc-lehner Feb 10, 2026
d461935
AP-25245: consistent nameing of python environment port and methods
marc-lehner Feb 11, 2026
b2afed7
AP-25245: rename pythonprocessprovider to externalprocessprovider
marc-lehner Feb 12, 2026
8ba9737
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
6916163
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
0e0a587
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
75264f0
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
aed540d
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
0e150ca
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
4d32cbd
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
97d8a99
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
e11c36a
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
565e579
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
91734ff
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
117afb5
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
4e14218
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
313606c
AP-25245: Fixup
HedgehogCode Feb 12, 2026
470e5e0
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
448dcf1
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
21c860d
AP-25245: FIXUP
HedgehogCode Feb 12, 2026
6230861
AP-25245: WIP
HedgehogCode Feb 12, 2026
6441929
AP-25245: WIP
HedgehogCode Feb 12, 2026
276cf92
AP-25245: WIP
HedgehogCode Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion org.knime.python3.arrow.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ Require-Bundle: org.junit;bundle-version="[4.13.0,5.0.0)",
org.apache.commons.lang3;bundle-version="[3.9.0,4.0.0)",
org.knime.core.columnar;bundle-version="[5.6.0,6.0.0)",
org.knime.core.data.columnar;bundle-version="[5.6.0,6.0.0)",
org.knime.python3.testing;bundle-version="[5.6.0,6.0.0)"
org.knime.python3.testing;bundle-version="[5.6.0,6.0.0)",
org.knime.externalprocessprovider;bundle-version="[5.11.0,6.0.0)"
Automatic-Module-Name: org.knime.python3.arrow.tests
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
import java.util.Collections;
import java.util.List;

import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.DefaultPythonGateway;
import org.knime.python3.Python3SourceDirectory;
import org.knime.python3.PythonCommand;
import org.knime.python3.PythonDataSink;
import org.knime.python3.PythonDataSource;
import org.knime.python3.PythonEntryPoint;
Expand Down Expand Up @@ -83,7 +83,7 @@ private TestUtils() {
* @throws InterruptedException
*/
public static PythonGateway<ArrowTestsEntryPoint> openPythonGateway() throws IOException, InterruptedException {
final PythonCommand command = Python3TestUtils.getPythonCommand();
final ExternalProcessProvider command = Python3TestUtils.getPythonCommand();
final String launcherPath =
Paths.get(System.getProperty("user.dir"), "src/test/python", "tests_launcher.py").toString();
final PythonPath pythonPath = (new PythonPathBuilder()) //
Expand Down
3 changes: 2 additions & 1 deletion org.knime.python3.arrow.types.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Require-Bundle: org.knime.core.table;bundle-version="[5.6.0,6.0.0)",
org.knime.core.data.columnar;bundle-version="[5.6.0,6.0.0)",
com.fasterxml.jackson.core.jackson-databind;bundle-version="[2.12.1,3.0.0)",
org.knime.python3.types;bundle-version="[5.6.0,6.0.0)",
org.knime.python3.testing;bundle-version="[5.6.0,6.0.0)"
org.knime.python3.testing;bundle-version="[5.6.0,6.0.0)",
org.knime.externalprocessprovider;bundle-version="[5.11.0,6.0.0)"
Automatic-Module-Name: org.knime.python3.arrow.types.tests
Export-Package: org.knime.python3.arrow.types
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
import org.knime.core.table.schema.DataSpecs.DataSpecWithTraits;
import org.knime.core.table.schema.DefaultColumnarSchema;
import org.knime.core.table.schema.traits.LogicalTypeTrait;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.filehandling.core.connections.FSCategory;
import org.knime.filehandling.core.connections.FSLocation;
import org.knime.filehandling.core.data.location.FSLocationValue;
Expand All @@ -172,7 +173,6 @@
import org.knime.filehandling.core.data.location.cell.SimpleFSLocationCellFactory;
import org.knime.python3.DefaultPythonGateway;
import org.knime.python3.Python3SourceDirectory;
import org.knime.python3.PythonCommand;
import org.knime.python3.PythonDataSink;
import org.knime.python3.PythonDataSource;
import org.knime.python3.PythonEntryPoint;
Expand Down Expand Up @@ -893,7 +893,7 @@ interface TriConsumer<A, B, C> {

private static <E extends PythonEntryPoint> PythonGateway<E> openPythonGateway(final Class<E> entryPointClass,
final String launcherModule, final PythonModule... modules) throws IOException, InterruptedException {
final PythonCommand command = Python3TestUtils.getPythonCommand();
final ExternalProcessProvider command = Python3TestUtils.getPythonCommand();
final String launcherPath = Paths.get(System.getProperty("user.dir"), "src/test/python", launcherModule)
.toString();
final PythonPathBuilder builder = PythonPath.builder()//
Expand Down
3 changes: 2 additions & 1 deletion org.knime.python3.nodes/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ Require-Bundle: org.knime.core;bundle-version="[5.10.0,6.0.0)",
com.fasterxml.jackson.core.jackson-annotations;bundle-version="[2.13.2,3.0.0)",
org.knime.workflowservices;bundle-version="[5.10.0,6.0.0)",
org.apache.commons.lang3;bundle-version="[3.9.0,4.0.0)",
org.knime.gateway.impl;bundle-version="[5.10.0,6.0.0)"
org.knime.gateway.impl;bundle-version="[5.10.0,6.0.0)",
org.knime.externalprocessprovider;bundle-version="[5.11.0,6.0.0)"
Automatic-Module-Name: org.knime.python3.nodes
Eclipse-RegisterBuddy: org.knime.ext.py4j
Eclipse-BundleShape: dir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@

import org.knime.conda.prefs.CondaPreferences;
import org.knime.core.node.NodeLogger;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.CondaPythonCommand;
import org.knime.python3.PythonCommand;
import org.knime.python3.SimplePythonCommand;
import org.yaml.snakeyaml.Yaml;

Expand Down Expand Up @@ -91,7 +91,7 @@ static Stream<Path> getPathsToCustomExtensions() {
.map(Optional::get);
}

static Optional<PythonCommand> getCustomPythonCommand(final String extensionId) {
static Optional<ExternalProcessProvider> getCustomPythonCommand(final String extensionId) {
return loadConfigs()//
.filter(e -> extensionId.equals(e.m_id))//
.findFirst()//
Expand Down Expand Up @@ -307,7 +307,7 @@ Optional<Path> getSrcPath() {
}
}

Optional<PythonCommand> getCommand() {
Optional<ExternalProcessProvider> getCommand() {
if (m_condaEnvPath != null) {
if (m_pythonExecutable != null) {
LOGGER.warnWithFormat("Both conda_env_path and python_executable are provided for extension '%s'."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@
import java.util.Objects;

import org.knime.conda.envbundling.environment.CondaEnvironmentRegistry;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.Activator;
import org.knime.python3.BundledPythonCommand;
import org.knime.python3.FreshPythonGatewayFactory;
import org.knime.python3.Python3SourceDirectory;
import org.knime.python3.PythonCommand;
import org.knime.python3.PythonEntryPointUtils;
import org.knime.python3.PythonGateway;
import org.knime.python3.PythonGatewayFactory;
Expand Down Expand Up @@ -141,12 +141,12 @@ public PythonGateway<KnimeNodeBackend> create() throws IOException, InterruptedE
return gateway;
}

private static PythonCommand createCommand(final String extensionId, final String environmentName) {
private static ExternalProcessProvider createCommand(final String extensionId, final String environmentName) {
return PythonExtensionPreferences.getCustomPythonCommand(extensionId)//
.orElseGet(() -> getPythonCommandForEnvironment(environmentName));
}

private static PythonCommand getPythonCommandForEnvironment(final String environmentName) {
private static ExternalProcessProvider getPythonCommandForEnvironment(final String environmentName) {
final var environment = CondaEnvironmentRegistry.getEnvironment(environmentName);
if (environment == null) {
throw new IllegalStateException("Conda environment '" + environmentName + "' not found. "
Expand Down
8 changes: 5 additions & 3 deletions org.knime.python3.scripting.nodes/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ Require-Bundle: org.knime.core;bundle-version="[5.11.0,6.0.0)",
org.eclipse.ui;bundle-version="3.119.0",
org.knime.conda;bundle-version="[5.11.0,6.0.0)",
org.knime.conda.envbundling;bundle-version="[5.10.0,6.0.0)",
org.knime.core.ui;bundle-version="[5.11.0,6.0.0)",
org.knime.workbench.editor;bundle-version="[5.10.0,6.0.0)",
org.knime.pixi.port;bundle-version="[5.11.0,6.0.0)",
org.knime.core.ui;bundle-version="[5.10.0,6.0.0)",
org.knime.workbench.editor;bundle-version="[5.9.0,6.0.0)",
Comment on lines +27 to +28
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The minimum version requirement for org.knime.core.ui has been lowered from 5.11.0 to 5.10.0, and for org.knime.workbench.editor from 5.10.0 to 5.9.0. These changes appear unrelated to the PR's purpose of adding pixi environment support. Verify that these version downgrades are intentional and necessary, as they may allow the bundle to run with older, potentially incompatible versions of these dependencies.

Suggested change
org.knime.core.ui;bundle-version="[5.10.0,6.0.0)",
org.knime.workbench.editor;bundle-version="[5.9.0,6.0.0)",
org.knime.core.ui;bundle-version="[5.11.0,6.0.0)",
org.knime.workbench.editor;bundle-version="[5.10.0,6.0.0)",

Copilot uses AI. Check for mistakes.
org.apache.batik.util;bundle-version="[1.16.0,2.0.0)",
org.apache.batik.dom;bundle-version="[1.16.0,2.0.0)",
org.apache.batik.anim;bundle-version="[1.16.0,2.0.0)",
Expand All @@ -38,7 +39,8 @@ Require-Bundle: org.knime.core;bundle-version="[5.11.0,6.0.0)",
com.fasterxml.jackson.core.jackson-core;bundle-version="2.13.2",
com.fasterxml.jackson.core.jackson-databind;bundle-version="2.13.2",
org.eclipse.orbit.xml-apis-ext;bundle-version="[1.0.0,2.0.0)",
org.apache.commons.commons-io;bundle-version="[2.15.1,3.0.0)"
org.apache.commons.commons-io;bundle-version="[2.15.1,3.0.0)",
org.knime.externalprocessprovider;bundle-version="[5.11.0,6.0.0)"
Automatic-Module-Name: org.knime.python3.scripting.nodes
Export-Package: org.knime.python3.scripting.nodes.prefs
Eclipse-RegisterBuddy: org.knime.ext.py4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
import org.knime.core.util.PathUtils;
import org.knime.core.util.asynclose.AsynchronousCloseableTracker;
import org.knime.core.webui.node.view.NodeView;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python2.PythonCommand;
import org.knime.python2.PythonModuleSpec;
import org.knime.python2.PythonVersion;
Expand Down Expand Up @@ -381,14 +382,14 @@ private void pushNewFlowVariable(final FlowVariable variable) {
}

/**
* Wraps a {@link org.knime.python3.PythonCommand} into the legacy implementation for using it in a
* Wraps a {@link ExternalProcessProvider} into the legacy implementation for using it in a
* {@link PythonKernelBackend}.
*/
private static final class LegacyPythonCommand implements PythonCommand {

private final org.knime.python3.PythonCommand m_pythonCommand;
private final ExternalProcessProvider m_pythonCommand;

private LegacyPythonCommand(final org.knime.python3.PythonCommand pythonCommand) {
private LegacyPythonCommand(final ExternalProcessProvider pythonCommand) {
m_pythonCommand = pythonCommand;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@

import org.knime.core.node.BufferedDataTable;
import org.knime.core.node.context.ports.PortsConfiguration;
import org.knime.core.node.port.PortObject;
import org.knime.core.node.port.PortType;
import org.knime.core.node.port.image.ImagePortObject;
import org.knime.pixi.port.PythonEnvironmentPortObject;
import org.knime.python2.port.PickledObjectFileStorePortObject;
import org.knime.python2.ports.DataTableInputPort;
import org.knime.python2.ports.DataTableOutputPort;
Expand All @@ -61,6 +63,7 @@
import org.knime.python2.ports.PickledObjectInputPort;
import org.knime.python2.ports.PickledObjectOutputPort;


/**
* @author Marcel Wiedenmann, KNIME GmbH, Konstanz, Germany
* @author Benjamin Wilhelm, KNIME GmbH, Berlin, Germany
Expand All @@ -71,6 +74,27 @@ private PortsConfigurationUtils() {
// Utility class
}

/**
* Check if the ports configuration contains a Python environment port.
*
* @param config the ports configuration
* @return true if a Python environment port is present
*/
public static boolean hasPythonEnvironmentPort(final PortsConfiguration config) {
final PortType[] inTypes = config.getInputPorts();
try {
// Check if any input port is a PythonEnvironmentPortObject
for (final PortType inType : inTypes) {
if (isPythonEnvironmentPort(inType)) {
return true;
}
}
} catch (NoClassDefFoundError e) {
// Python environment bundle is not available - this is fine since it's optional
}
return false;
}

/**
* Extract the input ports from the given ports configuration.
*
Expand All @@ -81,9 +105,21 @@ public static InputPort[] createInputPorts(final PortsConfiguration config) {
final PortType[] inTypes = config.getInputPorts();
int inTableIndex = 0;
int inObjectIndex = 0;
final var inPorts = new InputPort[inTypes.length];
// Count non-environment ports for the result array
int numNonEnvironmentPorts = 0;
for (final PortType inType : inTypes) {
if (!isPythonEnvironmentPort(inType)) {
numNonEnvironmentPorts++;
}
}
final var inPorts = new InputPort[numNonEnvironmentPorts];
int portIndex = 0;
for (int i = 0; i < inTypes.length; i++) {
final PortType inType = inTypes[i];
// Skip Python environment ports - they are not InputPorts in the traditional sense
if (isPythonEnvironmentPort(inType)) {
continue;
}
final InputPort inPort;
if (BufferedDataTable.TYPE.equals(inType)) {
inPort = new DataTableInputPort("knio.input_tables[" + inTableIndex++ + "]");
Expand All @@ -92,11 +128,26 @@ public static InputPort[] createInputPorts(final PortsConfiguration config) {
} else {
throw new IllegalStateException("Unsupported input type: " + inType.getName());
}
inPorts[i] = inPort;
inPorts[portIndex++] = inPort;
}
return inPorts;
}

/**
* Check if a port type is a Python environment port.
*
* @param inType the port type to check
* @return true if the port type is a Python environment port
*/
public static boolean isPythonEnvironmentPort(final PortType inType) {
try {
return inType.equals(PythonEnvironmentPortObject.TYPE) || inType.equals(PythonEnvironmentPortObject.TYPE_OPTIONAL);
} catch (NoClassDefFoundError e) {
// Python environment bundle is not available - this is fine since it's optional
return false;
}
}

/**
* Extract the output ports from the given ports configuration.
*
Expand Down Expand Up @@ -135,4 +186,48 @@ public static OutputPort[] createOutputPorts(final PortsConfiguration config) {
public static OutputPort createPickledObjectOutputPort(final int outObjectSuffix) {
return new PickledObjectOutputPort("knio.output_objects[" + outObjectSuffix + "]");
}
}

/**
* Extract the Python environment port object from the input port objects, if present.
*
* @param inObjects the input port objects
* @return the Python environment port object, or null if not present
* @throws IllegalArgumentException if a Python environment port is expected but not found
*/
public static PythonEnvironmentPortObject extractPythonEnvironmentPort(final PortObject[] inObjects) {
for (final PortObject inObject : inObjects) {
if (inObject instanceof PythonEnvironmentPortObject envPort) {
return envPort;
}
}
throw new IllegalArgumentException(
"Expected a Python environment port object in the input ports, but none was found.");
}

/*public static void installPythonEnvironmentIfPresent(final PortsConfiguration config, final PortObject[] inObjects,
final ExecutionMonitor exec) throws IOException, CanceledExecutionException {
final PythonEnvironmentPortObject envPort = extractPythonEnvironmentPort(config, inObjects);
if (envPort != null) {
PythonEnvironmentPortObject.installPythonEnvironmentWithProgress(config, inObjects, exec);
}
}*/

Comment on lines +207 to +214
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this commented-out code block. Dead code should be eliminated rather than left in comments, especially in a version-controlled codebase.

Suggested change
/*public static void installPythonEnvironmentIfPresent(final PortsConfiguration config, final PortObject[] inObjects,
final ExecutionMonitor exec) throws IOException, CanceledExecutionException {
final PythonEnvironmentPortObject envPort = extractPythonEnvironmentPort(config, inObjects);
if (envPort != null) {
PythonEnvironmentPortObject.installPythonEnvironmentWithProgress(config, inObjects, exec);
}
}*/

Copilot uses AI. Check for mistakes.
/**
* Filter out the Python environment port from the input port objects array.
* The environment port is not a data port and should not be passed to the session.
*
* @param inObjects the input port objects
* @return the filtered array without the Python environment port
*/
public static PortObject[] filterEnvironmentPort(final PortObject[] inObjects) {
// Find and exclude the environment port
final PortObject[] filtered = new PortObject[inObjects.length - 1];
int filteredIndex = 0;
for (int i = 0; i < inObjects.length; i++) {
if (!(inObjects[i] instanceof PythonEnvironmentPortObject)) {
filtered[filteredIndex++] = inObjects[i];
Comment on lines +223 to +228
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code assumes exactly one environment port exists, but doesn't validate this assumption. If there are zero or multiple environment ports, this will create an incorrectly-sized array and may lead to ArrayIndexOutOfBoundsException.

Suggested change
// Find and exclude the environment port
final PortObject[] filtered = new PortObject[inObjects.length - 1];
int filteredIndex = 0;
for (int i = 0; i < inObjects.length; i++) {
if (!(inObjects[i] instanceof PythonEnvironmentPortObject)) {
filtered[filteredIndex++] = inObjects[i];
// First count how many environment ports are present
int envPortCount = 0;
for (final PortObject inObject : inObjects) {
if (inObject instanceof PythonEnvironmentPortObject) {
envPortCount++;
}
}
// If there is no environment port, nothing to filter out
if (envPortCount == 0) {
return inObjects;
}
// Find and exclude all environment ports
final PortObject[] filtered = new PortObject[inObjects.length - envPortCount];
int filteredIndex = 0;
for (final PortObject inObject : inObjects) {
if (!(inObject instanceof PythonEnvironmentPortObject)) {
filtered[filteredIndex++] = inObject;

Copilot uses AI. Check for mistakes.
}
}
Comment on lines 224 to 230
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filterEnvironmentPort assumes exactly one environment port and blindly allocates inObjects.length - 1. If the configuration ever contains 0 or >1 environment ports (e.g., future changes, misconfiguration, or different port typing), this can either drop a data port or return an array containing trailing nulls (risking downstream NPEs). Allocate the filtered array by counting non-environment ports (based on inTypes) and fill it accordingly, or build a dynamic list and convert to array to guarantee correct sizing.

Suggested change
final PortObject[] filtered = new PortObject[inObjects.length - 1];
int filteredIndex = 0;
for (int i = 0; i < inTypes.length && i < inObjects.length; i++) {
if (!isPythonEnvironmentPort(inTypes[i])) {
filtered[filteredIndex++] = inObjects[i];
}
}
final int max = Math.min(inTypes.length, inObjects.length);
// First pass: count non-environment ports within the overlapping range
int nonEnvCount = 0;
for (int i = 0; i < max; i++) {
if (!isPythonEnvironmentPort(inTypes[i])) {
nonEnvCount++;
}
}
// Any remaining PortObjects beyond the typed range are treated as non-environment ports
if (inObjects.length > max) {
nonEnvCount += inObjects.length - max;
}
final PortObject[] filtered = new PortObject[nonEnvCount];
int filteredIndex = 0;
// Second pass: copy non-environment ports from the overlapping range
for (int i = 0; i < max; i++) {
if (!isPythonEnvironmentPort(inTypes[i])) {
filtered[filteredIndex++] = inObjects[i];
}
}
// Third pass: copy any remaining ports beyond the typed range
for (int i = max; i < inObjects.length; i++) {
filtered[filteredIndex++] = inObjects[i];
}

Copilot uses AI. Check for mistakes.
return filtered;
}
Comment on lines +222 to +232
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filterEnvironmentPort method assumes exactly one environment port exists and creates an array with length - 1. If there's no environment port in the array, this will create an array that's too small and cause an ArrayIndexOutOfBoundsException. The method should either count the number of environment ports first or check if any exist before creating the filtered array.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
import org.knime.conda.prefs.CondaPreferences;
import org.knime.core.node.NodeLogger;
import org.knime.core.node.defaultnodesettings.SettingsModelString;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.CondaPythonCommand;
import org.knime.python3.PythonCommand;

/**
* Copied and modified from org.knime.python2.config.
Expand Down Expand Up @@ -113,7 +113,7 @@ public ObservableValue<CondaEnvironmentIdentifier[]> getAvailableEnvironments()
}

@Override
public PythonCommand getPythonCommand() {
public ExternalProcessProvider getPythonCommand() {
return new CondaPythonCommand(CondaPreferences.getCondaInstallationDirectory(),
m_environmentDirectory.getStringValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
package org.knime.python3.scripting.nodes.prefs;

import org.knime.core.node.defaultnodesettings.SettingsModelString;
import org.knime.python3.PythonCommand;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.SimplePythonCommand;

/**
Expand Down Expand Up @@ -79,7 +79,7 @@ public SettingsModelString getExecutablePath() {
}

@Override
public PythonCommand getPythonCommand() {
public ExternalProcessProvider getPythonCommand() {
return new SimplePythonCommand(m_pythonPath.getStringValue());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.knime.conda.CondaEnvironmentIdentifier;
import org.knime.conda.prefs.CondaPreferences;
import org.knime.externalprocessprovider.ExternalProcessProvider;
import org.knime.python3.BundledPythonCommand;
import org.knime.python3.PythonCommand;

/**
* Convenience front-end of the preference-based configuration of the Python integration.
Expand Down Expand Up @@ -110,7 +110,7 @@ public static PythonEnvironmentType getEnvironmentTypePreference() {
/**
* @return The currently selected default Python command.
*/
public static PythonCommand getPythonCommandPreference() {
public static ExternalProcessProvider getPythonCommandPreference() {
final var envType = getEnvironmentTypePreference();
PythonEnvironmentsConfig environmentsConfig;

Expand All @@ -124,7 +124,7 @@ public static PythonCommand getPythonCommandPreference() {
}

/**
* @return The {@link PythonCommand} for the installed bundled environment.
* @return The {@link ExternalProcessProvider} for the installed bundled environment.
*/
public static BundledPythonCommand getBundledPythonCommand() {
return getBundledCondaEnvironmentConfig().getPythonCommand();
Expand Down
Loading
Loading