Skip to content
This repository was archived by the owner on Nov 23, 2023. It is now read-only.

Commit aa268d5

Browse files
authored
Merge pull request #9 from 3D-e-Chem/knime-43
Support KNIME 4.3
2 parents b30b261 + 3146f76 commit aa268d5

File tree

12 files changed

+156
-93
lines changed

12 files changed

+156
-93
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ The file is formatted as described on http://keepachangelog.com/.
55

66
## [Unreleased]
77

8+
## [2.0.4] - 2020-01-14
9+
10+
### Fixed
11+
12+
* Support KNIME 4.3 ([#9](https://github.com/3D-e-Chem/knime-python-wrapper/pull/9) + [#10](https://github.com/3D-e-Chem/knime-python-wrapper/pull/10))
13+
14+
### Removed
15+
16+
* Support for KNIME versions older than 4.3
17+
818
## [2.0.3] - 2019-07-02
919

1020
### Changed
@@ -53,3 +63,11 @@ The file is formatted as described on http://keepachangelog.com/.
5363

5464
* Abstract PythonWrapperNode classes
5565
* Test utility to run tests which call PythonKernel execute
66+
67+
[Unreleased]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v2.0.4...HEAD
68+
[2.0.4]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v2.0.3...v2.0.4
69+
[2.0.3]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v2.0.2...v2.0.3
70+
[2.0.2]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v2.0.1...v2.0.2
71+
[2.0.1]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v1.1.0...v2.0.1
72+
[1.1.0]: https://github.com/3D-e-Chem/knime-python-wrapper/compare/v1.0.0...v1.1.0
73+
[1.0.0]: https://github.com/3D-e-Chem/knime-python-wrapper/releases/tag/v1.0.0

p2/category.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<site>
3-
<bundle id="nl.esciencecenter.e3dchem.python.plugin" version="2.0.3.qualifier">
3+
<bundle id="nl.esciencecenter.e3dchem.python.plugin" version="2.0.4.qualifier">
44
<category name="nl.esciencecenter.3D-e-Chem"/>
55
</bundle>
66
<category-def name="nl.esciencecenter.3D-e-Chem" label="KNIME 3D-e-Chem Contributions">

p2/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<artifactId>nl.esciencecenter.e3dchem.python</artifactId>
66
<groupId>nl.esciencecenter.e3dchem.python</groupId>
7-
<version>2.0.3-SNAPSHOT</version>
7+
<version>2.0.4-SNAPSHOT</version>
88
</parent>
99
<artifactId>nl.esciencecenter.e3dchem.python.p2</artifactId>
1010
<packaging>eclipse-repository</packaging>

plugin/META-INF/MANIFEST.MF

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: Abstract Python wrapper KNIME node and helpers
44
Bundle-SymbolicName: nl.esciencecenter.e3dchem.python.plugin;singleton:=true
5-
Bundle-Version: 2.0.3.qualifier
5+
Bundle-Version: 2.0.4.qualifier
66
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
77
Bundle-Vendor: Netherlands eScience Center
8-
Require-Bundle: org.knime.core;bundle-version="[4.0.0,5.0.0)",
9-
org.knime.base;bundle-version="[4.0.0,5.0.0)",
10-
org.knime.python2;bundle-version="[4.0.0,5.0.0)",
11-
org.knime.python2.serde.csv;bundle-version="[4.0.0,5.0.0)",
12-
org.knime.python2.serde.flatbuffers;bundle-version="[4.0.0,5.0.0)",
13-
org.knime.python2.serde.arrow;bundle-version="[4.0.0,5.0.0)",
14-
org.knime.python2.serde.arrow.libs;bundle-version="[4.0.0,5.0.0)"
8+
Require-Bundle: org.knime.core;bundle-version="[4.3.0,5.0.0)",
9+
org.knime.base;bundle-version="[4.3.0,5.0.0)",
10+
org.knime.python2;bundle-version="[4.3.0,5.0.0)",
11+
org.knime.python2.envconfigs;bundle-version="[4.3.0,5.0.0)",
12+
org.knime.python2.serde.csv;bundle-version="[4.3.0,5.0.0)",
13+
org.knime.python2.serde.flatbuffers;bundle-version="[4.3.0,5.0.0)",
14+
org.knime.python2.serde.arrow;bundle-version="[4.3.0,5.0.0)"
1515
Bundle-ClassPath: .
1616
Export-Package: nl.esciencecenter.e3dchem.python

plugin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>nl.esciencecenter.e3dchem.python</groupId>
66
<artifactId>nl.esciencecenter.e3dchem.python</artifactId>
7-
<version>2.0.3-SNAPSHOT</version>
7+
<version>2.0.4-SNAPSHOT</version>
88
</parent>
99
<artifactId>nl.esciencecenter.e3dchem.python.plugin</artifactId>
1010
<packaging>eclipse-plugin</packaging>

plugin/src/java/nl/esciencecenter/e3dchem/python/PythonWrapperNodeConfig.java

Lines changed: 40 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
package nl.esciencecenter.e3dchem.python;
22

3-
import java.util.Arrays;
4-
import java.util.Collection;
5-
import java.util.Collections;
63
import java.util.HashSet;
74
import java.util.Locale;
8-
import java.util.Objects;
95
import java.util.Set;
106

11-
import org.knime.python2.DefaultPythonCommand;
7+
import org.knime.core.node.InvalidSettingsException;
8+
import org.knime.core.node.NodeSettingsRO;
9+
import org.knime.core.node.NodeSettingsWO;
10+
import org.knime.core.node.workflow.FlowVariable;
1211
import org.knime.python2.PythonCommand;
1312
import org.knime.python2.PythonModuleSpec;
1413
import org.knime.python2.PythonVersion;
14+
import org.knime.python2.config.PythonCommandFlowVariableConfig;
1515
import org.knime.python2.extensions.serializationlibrary.SentinelOption;
1616
import org.knime.python2.extensions.serializationlibrary.SerializationOptions;
1717
import org.knime.python2.generic.VariableNames;
1818
import org.knime.python2.kernel.PythonKernelOptions;
1919
import org.knime.python2.prefs.PythonPreferences;
20-
import org.knime.core.node.InvalidSettingsException;
21-
import org.knime.core.node.NodeSettingsRO;
22-
import org.knime.core.node.NodeSettingsWO;
23-
import org.knime.core.node.workflow.FlowVariable;
2420

2521
/**
2622
* Configuration for {@link PythonWrapperNodeModel}.
@@ -31,32 +27,22 @@
3127
*/
3228
public class PythonWrapperNodeConfig {
3329
private static final String CFG_PYTHON_VERSION_OPTION = "pythonVersionOption";
34-
static final String CFG_PYTHON2COMMAND = "python2Command";
35-
static final String CFG_PYTHON3COMMAND = "python3Command";
30+
private static final String CFG_PYTHON2_COMMAND = "python2Command";
31+
32+
private static final String CFG_PYTHON3_COMMAND = "python3Command";
3633
private static final String CFG_CONVERT_MISSING_TO_PYTHON = "convertMissingToPython";
3734
private static final String CFG_CONVERT_MISSING_FROM_PYTHON = "convertMissingFromPython";
3835
private static final String CFG_SENTINEL_OPTION = "sentinelOption";
3936
private static final String CFG_SENTINEL_VALUE = "sentinelValue";
4037
private static final String CFG_CHUNK_SIZE = "chunkSize";
4138

42-
/**
43-
* {@link #m_python2Command} and {@link #m_python3Command} are special in that they currently aren't configurable
44-
* via a Python scripting node's dialog but only using flow variables. If no respective flow variables are set,
45-
* their value is retrieved from the Python preferences.
46-
*/
47-
private static final String INDICATE_FALLBACK_TO_PREFERENCES_COMMAND_VALUE = "";
48-
4939
private PythonVersion m_pythonVersion = PythonPreferences.getPythonVersionPreference();
5040

51-
/**
52-
* {@code null} means to fall back to {@link PythonPreferences#getPython2CommandPreference()}.
53-
*/
54-
private PythonCommand m_python2Command = null;
41+
private PythonCommandFlowVariableConfig m_python2CommandConfig = new PythonCommandFlowVariableConfig(CFG_PYTHON2_COMMAND,
42+
PythonVersion.PYTHON2, PythonPreferences::getCondaInstallationPath);
5543

56-
/**
57-
* {@code null} means to fall back to {@link PythonPreferences#getPython2CommandPreference()}.
58-
*/
59-
private PythonCommand m_python3Command = null;
44+
private PythonCommandFlowVariableConfig m_python3CommandConfig = new PythonCommandFlowVariableConfig(CFG_PYTHON3_COMMAND,
45+
PythonVersion.PYTHON3, PythonPreferences::getCondaInstallationPath);
6046

6147
private int m_chunkSize = SerializationOptions.DEFAULT_CHUNK_SIZE;
6248

@@ -118,13 +104,20 @@ public void setPythonVersion(final PythonVersion pythonVersion) {
118104
m_pythonVersion = pythonVersion;
119105
}
120106

107+
/**
108+
* @return The config of the Python 2 command.
109+
*/
110+
public PythonCommandFlowVariableConfig getPython2CommandConfig() {
111+
return m_python2CommandConfig;
112+
}
113+
121114
/**
122115
* @return The Python 2 command to use. May be {@code null} in which case no specific Python 2 command is configured
123116
* and one has to resort to - e.g., - the {@link PythonPreferences#getPython2CommandPreference() global
124117
* preferences}.
125118
*/
126119
public PythonCommand getPython2Command() {
127-
return m_python2Command;
120+
return m_python2CommandConfig.getCommand().orElse(null);
128121
}
129122

130123
/**
@@ -133,16 +126,23 @@ public PythonCommand getPython2Command() {
133126
* {@link PythonPreferences#getPython2CommandPreference() global preferences}.
134127
*/
135128
public void setPython2Command(final PythonCommand python2Command) {
136-
m_python2Command = python2Command;
129+
m_python2CommandConfig.setCommand(python2Command);
130+
}
131+
132+
/**
133+
* @return The config of the Python 3 command.
134+
*/
135+
public PythonCommandFlowVariableConfig getPython3CommandConfig() {
136+
return m_python3CommandConfig;
137137
}
138138

139139
/**
140140
* @return The Python 3 command to use. May be {@code null} in which case no specific Python 3 command is configured
141141
* and one has to resort to - e.g., - the {@link PythonPreferences#getPython3CommandPreference() global
142142
* preferences}.
143143
*/
144-
public PythonCommand getPython3Command() {
145-
return m_python3Command;
144+
public PythonCommand getPython3Command() {
145+
return m_python3CommandConfig.getCommand().orElse(null);
146146
}
147147

148148
/**
@@ -151,9 +151,9 @@ public PythonCommand getPython3Command() {
151151
* {@link PythonPreferences#getPython3CommandPreference() global preferences}.
152152
*/
153153
public void setPython3Command(final PythonCommand python3Command) {
154-
m_python3Command = python3Command;
154+
m_python3CommandConfig.setCommand(python3Command);
155155
}
156-
156+
157157
/**
158158
*
159159
* @return The configured number of rows to transfer to/from Python per chunk of an input/output table.
@@ -273,13 +273,13 @@ public void saveTo(final NodeSettingsWO settings) {
273273
*/
274274
public void saveToInDialog(NodeSettingsWO settings) {
275275
settings.addString(CFG_PYTHON_VERSION_OPTION, getPythonVersion().getId());
276-
settings.addString(CFG_PYTHON2COMMAND, commandToString(getPython2Command()));
277-
settings.addString(CFG_PYTHON3COMMAND, commandToString(getPython3Command()));
278276
settings.addInt(CFG_CHUNK_SIZE, getChunkSize());
279277
settings.addBoolean(CFG_CONVERT_MISSING_TO_PYTHON, getConvertMissingToPython());
280278
settings.addBoolean(CFG_CONVERT_MISSING_FROM_PYTHON, getConvertMissingFromPython());
281279
settings.addString(CFG_SENTINEL_OPTION, getSentinelOption().name());
282280
settings.addInt(CFG_SENTINEL_VALUE, getSentinelValue());
281+
m_python2CommandConfig.saveSettingsTo(settings);
282+
m_python3CommandConfig.saveSettingsTo(settings);
283283
}
284284

285285
/**
@@ -300,39 +300,22 @@ public void loadFrom(final NodeSettingsRO settings) throws InvalidSettingsExcept
300300
*
301301
* @param settings
302302
* The settings to load from
303+
* @throws InvalidSettingsException
303304
*/
304-
public void loadFromInDialog(final NodeSettingsRO settings) {
305+
public void loadFromInDialog(final NodeSettingsRO settings) throws InvalidSettingsException {
305306
final String pythonVersionString = settings.getString(CFG_PYTHON_VERSION_OPTION, getPythonVersion().getId());
306307
// Backward compatibility: old saved versions may be all upper case.
307308
setPythonVersion(PythonVersion.fromId(pythonVersionString.toLowerCase(Locale.ROOT)));
308-
final String python2CommandString =
309-
settings.getString(CFG_PYTHON2COMMAND, commandToString(getPython2Command()));
310-
setPython2Command(commandFromString(python2CommandString));
311-
final String python3CommandString =
312-
settings.getString(CFG_PYTHON3COMMAND, commandToString(getPython3Command()));
313-
setPython3Command(commandFromString(python3CommandString));
314309
setChunkSize(settings.getInt(CFG_CHUNK_SIZE, getChunkSize()));
315310
setConvertMissingToPython(settings.getBoolean(CFG_CONVERT_MISSING_TO_PYTHON, getConvertMissingToPython()));
316311
setConvertMissingFromPython(
317312
settings.getBoolean(CFG_CONVERT_MISSING_FROM_PYTHON, getConvertMissingFromPython()));
318313
setSentinelOption(SentinelOption.valueOf(settings.getString(CFG_SENTINEL_OPTION, getSentinelOption().name())));
319314
setSentinelValue(settings.getInt(CFG_SENTINEL_VALUE, getSentinelValue()));
315+
m_python2CommandConfig.loadSettingsFrom(settings);
316+
m_python3CommandConfig.loadSettingsFrom(settings);
320317
}
321318

322-
private static String commandToString(final PythonCommand command) {
323-
return command != null //
324-
? command.toString() //
325-
: INDICATE_FALLBACK_TO_PREFERENCES_COMMAND_VALUE;
326-
}
327-
328-
private static PythonCommand commandFromString(final String commandString) {
329-
return Objects.equals(commandString, INDICATE_FALLBACK_TO_PREFERENCES_COMMAND_VALUE) //
330-
? null //
331-
// TODO: This only works for ordinary paths ("manual configuration"), not for Conda directory + environment
332-
// name ("Conda configuration").
333-
: new DefaultPythonCommand(commandString);
334-
}
335-
336319
/**
337320
* Set of key/value pairs which inside Python script will be a dictionary
338321
* named by {@link #getOptionsName()}.
@@ -352,9 +335,8 @@ public Set<FlowVariable> getOptionsValues() {
352335
public PythonKernelOptions getKernelOptions() {
353336
final SerializationOptions serializationOptions = new SerializationOptions(getChunkSize(),
354337
getConvertMissingToPython(), getConvertMissingFromPython(), getSentinelOption(), getSentinelValue());
355-
PythonKernelOptions opt = new PythonKernelOptions(getPythonVersion(), getPython2Command(), getPython3Command(),
356-
serializationOptions);
357-
return opt.forAddedAdditionalRequiredModules(additionalRequiredModules).forExternalCustomPath(externalCustomPath);
338+
PythonKernelOptions opt = new PythonKernelOptions();
339+
return opt.forSerializationOptions(serializationOptions).forAddedAdditionalRequiredModules(additionalRequiredModules).forExternalCustomPath(externalCustomPath);
358340
}
359341

360342
/**

plugin/src/java/nl/esciencecenter/e3dchem/python/PythonWrapperNodeModel.java

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import java.io.InputStreamReader;
88
import java.util.Arrays;
99
import java.util.Collection;
10+
import java.util.Collections;
1011
import java.util.LinkedList;
1112
import java.util.Map;
13+
import java.util.Set;
1214
import java.util.stream.Collectors;
1315

1416
import org.knime.base.node.util.exttool.ExtToolOutputNodeModel;
@@ -22,14 +24,21 @@
2224
import org.knime.core.node.port.PortType;
2325
import org.knime.core.node.workflow.FlowVariable;
2426
import org.knime.core.node.workflow.FlowVariable.Type;
27+
import org.knime.python2.PythonCommand;
28+
import org.knime.python2.PythonModuleSpec;
29+
import org.knime.python2.config.PythonFlowVariableOptions;
30+
import org.knime.python2.kernel.PythonCancelable;
31+
import org.knime.python2.kernel.PythonCanceledExecutionException;
2532
import org.knime.python2.kernel.PythonExecutionMonitorCancelable;
33+
import org.knime.python2.kernel.PythonIOException;
2634
import org.knime.python2.kernel.PythonKernel;
35+
import org.knime.python2.kernel.PythonKernelOptions;
36+
import org.knime.python2.kernel.PythonKernelQueue;
2737

2838
/**
2939
* Implements a {@link NodeModel} for nodes that launch external Python script.
3040
*
31-
* @param <C>
32-
* Configuration
41+
* @param <C> Configuration
3342
*/
3443
public abstract class PythonWrapperNodeModel<C extends PythonWrapperNodeConfig> extends ExtToolOutputNodeModel {
3544
protected C m_config = createConfig();
@@ -48,14 +57,52 @@ protected final C getConfig() {
4857
return m_config;
4958
}
5059

60+
/**
61+
* Gets the kernel specific options.
62+
*
63+
* @return the kernel specific options
64+
*/
65+
protected PythonKernelOptions getKernelOptions() {
66+
final PythonKernelOptions options = getConfig().getKernelOptions();
67+
final String serializerId = new PythonFlowVariableOptions(getAvailableFlowVariables()).getSerializerId()
68+
.orElse(null);
69+
return options.forSerializationOptions(options.getSerializationOptions().forSerializerId(serializerId));
70+
}
71+
72+
// Below has been copied from KNIME Python node code (https://github.com/knime/knime-python/blob/analytics-platform/4.3.0/org.knime.python2.nodes/src/org/knime/python2/nodes/PythonNodeModel.java) and
73+
// adjusted.
74+
protected PythonKernel getNextKernelFromQueue(final PythonCancelable cancelable)
75+
throws PythonCanceledExecutionException, PythonIOException {
76+
return getNextKernelFromQueue(Collections.emptySet(), Collections.emptySet(), cancelable);
77+
}
78+
79+
protected PythonKernel getNextKernelFromQueue(final Set<PythonModuleSpec> requiredAdditionalModules,
80+
final PythonCancelable cancelable) throws PythonCanceledExecutionException, PythonIOException {
81+
return getNextKernelFromQueue(requiredAdditionalModules, Collections.emptySet(), cancelable);
82+
}
83+
84+
protected PythonKernel getNextKernelFromQueue(final Set<PythonModuleSpec> requiredAdditionalModules,
85+
final Set<PythonModuleSpec> optionalAdditionalModules, final PythonCancelable cancelable)
86+
throws PythonCanceledExecutionException, PythonIOException {
87+
final PythonKernelOptions options = getKernelOptions();
88+
final PythonCommand command = options.getUsePython3() //
89+
? options.getPython3Command() //
90+
: options.getPython2Command();
91+
return PythonKernelQueue.getNextKernel(command, requiredAdditionalModules, optionalAdditionalModules, options,
92+
cancelable);
93+
}
94+
5195
public BufferedDataTable[] execute(BufferedDataTable[] inData, ExecutionContext exec) throws Exception {
52-
// Below has been copied from Knime Python node source code and
96+
// Below has been copied from KNIME Python node source code (https://github.com/knime/knime-python/blob/analytics-platform/4.3.0/org.knime.python2.nodes/src/org/knime/python2/nodes/source/PythonSourceNodeModel.java) and
5397
// adjusted.
54-
PythonKernel kernel = new PythonKernel(getConfig().getKernelOptions());
55-
try {
56-
return executeKernel(inData, exec, kernel);
57-
} finally {
58-
kernel.close();
98+
final PythonExecutionMonitorCancelable cancelable = new PythonExecutionMonitorCancelable(exec);
99+
try (final PythonKernel kernel = getNextKernelFromQueue(cancelable)) {
100+
kernel.setOptions(getConfig().getKernelOptions());
101+
try {
102+
return executeKernel(inData, exec, kernel);
103+
} finally {
104+
kernel.close();
105+
}
59106
}
60107
}
61108

@@ -96,11 +143,10 @@ public BufferedDataTable[] executeKernel(BufferedDataTable[] inData, ExecutionCo
96143
/**
97144
* Push new variables to the stack.
98145
*
99-
* Only pushes new variables to the stack if they are new or changed in type
100-
* or value.
146+
* Only pushes new variables to the stack if they are new or changed in type or
147+
* value.
101148
*
102-
* @param newVariables
103-
* The flow variables to push
149+
* @param newVariables The flow variables to push
104150
*/
105151
protected void addNewVariables(Collection<FlowVariable> newVariables) {
106152
// Below has been copied from Knime Python node source code and

0 commit comments

Comments
 (0)