1 package org.simantics.pythonlink;
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.io.OutputStreamWriter;
7 import java.util.HashSet;
9 import java.util.concurrent.Callable;
10 import java.util.concurrent.ExecutionException;
11 import java.util.concurrent.ExecutorService;
12 import java.util.concurrent.Executors;
13 import java.util.regex.Pattern;
15 import org.simantics.databoard.Bindings;
16 import org.simantics.databoard.binding.Binding;
17 import org.simantics.databoard.binding.error.BindingException;
18 import org.simantics.databoard.binding.mutable.Variant;
19 import org.simantics.scl.runtime.SCLContext;
21 public class PythonContext implements Closeable {
22 protected static final String PYTHON_SCL_WRITER = "python.scl.writer";
23 //TODO Replace with a count of open contexts and call Py_Finalize when the last one closes.
24 private static Boolean isPyInitialized = false;
26 // A writer that is called by the sys.stdout and sys.stderr streams in Python
27 private static final Writer pythonWriter = new Writer() {
28 Writer defaultWriter = new OutputStreamWriter(System.out);
31 public void close() throws IOException {
32 throw new IllegalStateException("This writer should never be closed!");
36 public void flush() throws IOException {
37 Writer writer = getPythonWriter();
38 synchronized (writer) {
44 public void write(char[] cbuf, int off, int len) throws IOException {
45 Writer writer = getPythonWriter();
46 synchronized (writer) {
47 writer.write(cbuf, off, len);
51 // Get a thread-specific Writer instance
52 private Writer getPythonWriter() {
53 SCLContext sclContext = SCLContext.getCurrent();
54 Writer writer = (Writer) sclContext.get(PYTHON_SCL_WRITER);
55 return writer != null ? writer : defaultWriter;
59 private static synchronized void ensurePythonInit() {
60 if (!Activator.isPythonLoaded()) {
61 throw new PythonException("Python interpreter has not been successfully loaded. Check availablility of python3.dll in path.");
64 if (!isPyInitialized) {
65 execute(() -> initializePython(pythonWriter));
66 isPyInitialized = true;
70 public interface Listener {
71 void updated(String variableName);
75 Set<Listener> listeners = new HashSet<>();
77 public enum VariableType {
90 static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)");
92 // Really a C pointer.
93 private long contextID;
97 contextID = execute(() -> createContextImpl());
99 throw new PythonException("Python initialization has failed");
103 public void addListener(Listener listener) {
104 listeners.add(listener);
107 public void removeListener(Listener listener) {
108 listeners.remove(listener);
112 public synchronized void close() {
115 if (id != 0) execute(() -> deleteContextImpl(id));
117 for (Listener l : listeners) {
122 public boolean isOpen() {
123 return contextID != 0;
127 protected void finalize() throws Throwable {
132 public void executePythonStatement(String statement, Writer writer) {
133 SCLContext sclContext = SCLContext.getCurrent();
136 SCLContext.push(sclContext);
137 Writer oldWriter = (Writer) sclContext.put(PYTHON_SCL_WRITER, writer);
139 executePythonStatementImpl( contextID, statement );
140 pythonWriter.flush();
141 } catch (IOException e) {
144 if (oldWriter != null) {
145 sclContext.put(PYTHON_SCL_WRITER, oldWriter);
148 sclContext.remove(PYTHON_SCL_WRITER);
154 for (Listener l : listeners) { l.updated(null); }
157 public void executePythonStatement(String statement) {
158 executePythonStatement(statement, new SCLReportingWriter());
163 public void setPythonBooleanVariable(String variableName, boolean value) {
164 checkValidName(variableName);
165 execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value));
166 for (Listener l : listeners) { l.updated(variableName); }
169 public void setPythonIntegerVariable(String variableName, int value) {
170 checkValidName(variableName);
171 execute(() -> setPythonLongVariableImpl(contextID, variableName, value));
172 for (Listener l : listeners) { l.updated(variableName); }
174 public void setPythonLongVariable(String variableName, long value) {
175 checkValidName(variableName);
176 execute(() -> setPythonLongVariableImpl(contextID, variableName, value));
177 for (Listener l : listeners) { l.updated(variableName); }
179 public void setPythonDoubleVariable(String variableName, double value) {
180 checkValidName(variableName);
181 execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value));
182 for (Listener l : listeners) { l.updated(variableName); }
184 public void setPythonStringVariable(String variableName, String value) {
185 checkValidName(variableName);
186 execute(() -> setPythonStringVariableImpl(contextID, variableName, value));
187 for (Listener l : listeners) { l.updated(variableName); }
190 public void setPythonBooleanArrayVariable(String variableName, boolean[] value) {
191 checkValidName(variableName);
192 execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value));
193 for (Listener l : listeners) { l.updated(variableName); }
195 public void setPythonIntegerArrayVariable(String variableName, int[] value) {
196 checkValidName(variableName);
197 execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value));
198 for (Listener l : listeners) { l.updated(variableName); }
200 public void setPythonLongArrayVariable(String variableName, long[] value) {
201 checkValidName(variableName);
202 execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value));
203 for (Listener l : listeners) { l.updated(variableName); }
205 public void setPythonDoubleArrayVariable(String variableName, double[] value) {
206 checkValidName(variableName);
207 execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value));
208 for (Listener l : listeners) { l.updated(variableName); }
210 public void setPythonStringArrayVariable(String variableName, String[] value) {
211 checkValidName(variableName);
212 execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value));
213 for (Listener l : listeners) { l.updated(variableName); }
218 public boolean getPythonBooleanVariable(String variableName) {
219 checkValidName(variableName);
220 return getPythonBooleanVariableImpl(contextID, variableName);
222 public int getPythonIntegerVariable(String variableName) {
223 checkValidName(variableName);
224 long value = execute(() -> getPythonLongVariableImpl(contextID, variableName));
225 if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE)
226 throw new RuntimeException("Python value not in integer range");
229 public long getPythonLongVariable(String variableName) {
230 checkValidName(variableName);
231 return execute(() -> getPythonLongVariableImpl(contextID, variableName));
233 public double getPythonDoubleVariable(String variableName) {
234 checkValidName(variableName);
235 return execute(() -> getPythonDoubleVariableImpl(contextID, variableName));
237 public String getPythonStringVariable(String variableName) {
238 checkValidName(variableName);
239 return execute(() -> getPythonStringVariableImpl(contextID, variableName));
242 public boolean[] getPythonBooleanArrayVariable(String variableName) {
243 checkValidName(variableName);
244 return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName));
246 public int[] getPythonIntegerArrayVariable(String variableName) {
247 checkValidName(variableName);
248 return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName));
250 public long[] getPythonLongArrayVariable(String variableName) {
251 checkValidName(variableName);
252 return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName));
254 public double[] getPythonDoubleArrayVariable(String variableName) {
255 checkValidName(variableName);
256 return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName));
258 public String[] getPythonStringArrayVariable(String variableName) {
259 checkValidName(variableName);
260 return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName));
263 public void setPythonNDArrayVariable(String variableName, NDArray value) {
264 checkValidName(variableName);
265 execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value));
267 public NDArray getPythonNDArrayVariable(String variableName) {
268 checkValidName(variableName);
269 return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName));
272 public Object getPythonVariantVariable(String variableName, Binding binding) {
273 checkValidName(variableName);
274 Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName));
276 return Bindings.OBJECT.getContent(result, binding);
277 } catch (BindingException e) {
278 throw new RuntimeException(e);
282 public Variant getPythonVariantVariable(String variableName) {
283 checkValidName(variableName);
284 return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName)));
287 public void setPythonVariantVariable(String variableName, Variant value) {
288 setPythonVariantVariable(variableName, value.getValue(), value.getBinding());
291 public void setPythonVariantVariable(String variableName, Object value, Binding binding) {
292 checkValidName(variableName);
293 if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding");
295 execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding));
297 for (Listener l : listeners) { l.updated(variableName); }
300 public VariableType getPythonVariableType(String variableName) {
301 checkValidName(variableName);
302 int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName));
304 VariableType[] values = VariableType.values();
305 if (code < 0 || code >= values.length)
306 return VariableType.UNKNOWN;
311 public String[] getPythonVariableNames() {
312 return execute(() -> getPythonVariableNamesImpl(contextID));
315 private static void checkValidName(String variableName) {
316 if (!namePattern.matcher(variableName).matches())
317 throw new IllegalArgumentException("Invalid Python variable name " + variableName);
320 static final ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();
322 static void execute(Runnable job) {
324 pythonExecutor.submit(job).get();
325 } catch (InterruptedException e) {
326 throw new RuntimeException(e);
327 } catch (ExecutionException e) {
328 Throwable cause = e.getCause();
329 if (cause instanceof RuntimeException)
330 throw (RuntimeException) cause;
332 throw new RuntimeException(cause);
336 static <V> V execute(Callable<V> job) {
338 return pythonExecutor.submit(job).get();
339 } catch (InterruptedException e) {
340 throw new RuntimeException(e);
341 } catch (ExecutionException e) {
342 Throwable cause = e.getCause();
343 if (cause instanceof RuntimeException)
344 throw (RuntimeException) cause;
346 throw new RuntimeException(cause);
350 // Native function declarations
351 private static native void initializePython(Writer writer);
353 private static native long createContextImpl();
354 private static native void deleteContextImpl(long contextID);
356 private static native int executePythonStatementImpl(long contextID, String statement);
358 private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value);
359 private static native void setPythonLongVariableImpl(long contextID, String variableName, long value);
360 private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value);
361 private static native void setPythonStringVariableImpl(long contextID, String variableName, String value);
363 private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value);
364 private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value);
365 private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value);
366 private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value);
367 private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value);
369 private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName);
370 private static native long getPythonLongVariableImpl(long contextID, String variableName);
371 private static native double getPythonDoubleVariableImpl(long contextID, String variableName);
372 private static native String getPythonStringVariableImpl(long contextID, String variableName);
374 private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName);
375 private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName);
376 private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName);
377 private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName);
378 private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName);
380 private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value);
381 private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName);
383 private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding);
384 private static native Object getPythonVariantVariableImpl(long contextID, String variableName);
386 private static native int getPythonVariableTypeImpl(long contextID, String variableName);
388 private static native String[] getPythonVariableNamesImpl(long contextID);