X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.pythonlink%2Fsrc%2Forg%2Fsimantics%2Fpythonlink%2FPythonContext.java;h=77701ef5b352423e18d66ae90790dc296c178091;hb=d71490fa46106b0502ee3b903793a16a3bb6f68b;hp=5341cc233c16c02076e00bbfa8684aa09d14c001;hpb=41e86809c6f381612bdc8ffd4f4013aba39d1db1;p=simantics%2Fpython.git diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java index 5341cc2..77701ef 100644 --- a/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java @@ -1,104 +1,389 @@ -package org.simantics.pythonlink; - -import java.io.Closeable; - -public class PythonContext implements Closeable { - private long contextID; - - PythonContext() { - contextID = createContextImpl(); - } - - @Override - public void close() { - long id = contextID; - contextID = 0; - if (id != 0) deleteContextImpl(id); - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - close(); - } - - public void executePythonStatement(String statement) { - executePythonStatementImpl( contextID, statement ); - } - - // Setters - - public void setPythonIntegerVariable(String variableName, int value) { - setPythonIntegerVariableImpl(contextID, variableName, value); - } - public void setPythonDoubleVariable(String variableName, double value) { - setPythonDoubleVariableImpl(contextID, variableName, value); - } - public void setPythonStringVariable(String variableName, String value) { - setPythonStringVariableImpl(contextID, variableName, value); - } - - public void setPythonIntegerArrayVariable(String variableName, int[] value) { - setPythonIntegerArrayVariableImpl(contextID, variableName, value); - } - public void setPythonDoubleArrayVariable(String variableName, double[] value) { - setPythonDoubleArrayVariableImpl(contextID, variableName, value); - } - public void setPythonStringArrayVariable(String variableName, String[] value) { - setPythonStringArrayVariableImpl(contextID, variableName, value); - } - - // Getters - - public int getPythonIntegerVariable(String variableName) { - return getPythonIntegerVariableImpl(contextID, variableName); - } - public double getPythonDoubleVariable(String variableName) { - return getPythonDoubleVariableImpl(contextID, variableName); - } - public String getPythonStringVariable(String variableName) { - return getPythonStringVariableImpl(contextID, variableName); - } - - public int[] getPythonIntegerArrayVariable(String variableName) { - return getPythonIntegerArrayVariableImpl(contextID, variableName); - } - public double[] getPythonDoubleArrayVariable(String variableName) { - return getPythonDoubleArrayVariableImpl(contextID, variableName); - } - public String[] getPythonStringArrayVariable(String variableName) { - return getPythonStringArrayVariableImpl(contextID, variableName); - } - - public void setPythonNDArrayVariable(String variableName, NDArray value) { - setPythonNDArrayVariableImpl(contextID, variableName, value); - } - public NDArray getPythonNDArrayVariable(String variableName) { - return getPythonNDArrayVariableImpl(contextID, variableName); - } - - // Native function declarations - private static native long createContextImpl(); - private static native void deleteContextImpl(long contextID); - - private static native int executePythonStatementImpl(long contextID, String statement); - - private static native void setPythonIntegerVariableImpl(long contextID, String variableName, int value); - private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value); - private static native void setPythonStringVariableImpl(long contextID, String variableName, String value); - - private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value); - private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value); - private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value); - - private static native int getPythonIntegerVariableImpl(long contextID, String variableName); - private static native double getPythonDoubleVariableImpl(long contextID, String variableName); - private static native String getPythonStringVariableImpl(long contextID, String variableName); - - private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName); - private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName); - private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName); - - private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value); - private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName); -} +package org.simantics.pythonlink; + +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Pattern; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.Binding; +import org.simantics.databoard.binding.error.BindingException; +import org.simantics.databoard.binding.mutable.Variant; +import org.simantics.scl.runtime.SCLContext; + +public class PythonContext implements Closeable { + protected static final String PYTHON_SCL_WRITER = "python.scl.writer"; + //TODO Replace with a count of open contexts and call Py_Finalize when the last one closes. + private static Boolean isPyInitialized = false; + + // A writer that is called by the sys.stdout and sys.stderr streams in Python + private static final Writer pythonWriter = new Writer() { + Writer defaultWriter = new OutputStreamWriter(System.out); + + @Override + public void close() throws IOException { + throw new IllegalStateException("This writer should never be closed!"); + } + + @Override + public void flush() throws IOException { + Writer writer = getPythonWriter(); + synchronized (writer) { + writer.flush(); + } + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + Writer writer = getPythonWriter(); + synchronized (writer) { + writer.write(cbuf, off, len); + } + } + + // Get a thread-specific Writer instance + private Writer getPythonWriter() { + SCLContext sclContext = SCLContext.getCurrent(); + Writer writer = (Writer) sclContext.get(PYTHON_SCL_WRITER); + return writer != null ? writer : defaultWriter; + } + }; + + private static synchronized void ensurePythonInit() { + if (!Activator.isPythonLoaded()) { + throw new PythonException("Python interpreter has not been successfully loaded. Check availablility of python3.dll in path."); + } + + if (!isPyInitialized) { + execute(() -> initializePython(pythonWriter)); + isPyInitialized = true; + } + } + + public interface Listener { + void updated(String variableName); + void closed(); + } + + Set listeners = new HashSet<>(); + + public enum VariableType { + NO_VARIABLE, + BOOLEAN, + LONG, + FLOAT, + STRING, + BYTEARRAY, + DICTIONARY, + NDARRAY, + SEQUENCE, + UNKNOWN + } + + static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)"); + + // Really a C pointer. + private long contextID; + + PythonContext() { + ensurePythonInit(); + contextID = execute(() -> createContextImpl()); + if (contextID == 0) { + throw new PythonException("Python initialization has failed"); + } + } + + public void addListener(Listener listener) { + listeners.add(listener); + } + + public void removeListener(Listener listener) { + listeners.remove(listener); + } + + @Override + public synchronized void close() { + long id = contextID; + contextID = 0; + if (id != 0) execute(() -> deleteContextImpl(id)); + + for (Listener l : listeners) { + l.closed(); + } + } + + public boolean isOpen() { + return contextID != 0; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } + + public void executePythonStatement(String statement, Writer writer) { + SCLContext sclContext = SCLContext.getCurrent(); + + execute(() -> { + SCLContext.push(sclContext); + Writer oldWriter = (Writer) sclContext.put(PYTHON_SCL_WRITER, writer); + try { + executePythonStatementImpl( contextID, statement ); + pythonWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (oldWriter != null) { + sclContext.put(PYTHON_SCL_WRITER, oldWriter); + } + else { + sclContext.remove(PYTHON_SCL_WRITER); + } + } + SCLContext.pop(); + }); + + for (Listener l : listeners) { l.updated(null); } + } + + public void executePythonStatement(String statement) { + executePythonStatement(statement, new SCLReportingWriter()); + } + + // Setters + + public void setPythonBooleanVariable(String variableName, boolean value) { + checkValidName(variableName); + execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + + public void setPythonIntegerVariable(String variableName, int value) { + checkValidName(variableName); + execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonLongVariable(String variableName, long value) { + checkValidName(variableName); + execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonDoubleVariable(String variableName, double value) { + checkValidName(variableName); + execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonStringVariable(String variableName, String value) { + checkValidName(variableName); + execute(() -> setPythonStringVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + + public void setPythonBooleanArrayVariable(String variableName, boolean[] value) { + checkValidName(variableName); + execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonIntegerArrayVariable(String variableName, int[] value) { + checkValidName(variableName); + execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonLongArrayVariable(String variableName, long[] value) { + checkValidName(variableName); + execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonDoubleArrayVariable(String variableName, double[] value) { + checkValidName(variableName); + execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonStringArrayVariable(String variableName, String[] value) { + checkValidName(variableName); + execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + + // Getters + + public boolean getPythonBooleanVariable(String variableName) { + checkValidName(variableName); + return getPythonBooleanVariableImpl(contextID, variableName); + } + public int getPythonIntegerVariable(String variableName) { + checkValidName(variableName); + long value = execute(() -> getPythonLongVariableImpl(contextID, variableName)); + if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) + throw new RuntimeException("Python value not in integer range"); + return (int) value; + } + public long getPythonLongVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonLongVariableImpl(contextID, variableName)); + } + public double getPythonDoubleVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonDoubleVariableImpl(contextID, variableName)); + } + public String getPythonStringVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonStringVariableImpl(contextID, variableName)); + } + + public boolean[] getPythonBooleanArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName)); + } + public int[] getPythonIntegerArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName)); + } + public long[] getPythonLongArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName)); + } + public double[] getPythonDoubleArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName)); + } + public String[] getPythonStringArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName)); + } + + public void setPythonNDArrayVariable(String variableName, NDArray value) { + checkValidName(variableName); + execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value)); + } + public NDArray getPythonNDArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName)); + } + + public Object getPythonVariantVariable(String variableName, Binding binding) { + checkValidName(variableName); + Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName)); + try { + return Bindings.OBJECT.getContent(result, binding); + } catch (BindingException e) { + throw new RuntimeException(e); + } + } + + public Variant getPythonVariantVariable(String variableName) { + checkValidName(variableName); + return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName))); + } + + public void setPythonVariantVariable(String variableName, Variant value) { + setPythonVariantVariable(variableName, value.getValue(), value.getBinding()); + } + + public void setPythonVariantVariable(String variableName, Object value, Binding binding) { + checkValidName(variableName); + if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding"); + + execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding)); + + for (Listener l : listeners) { l.updated(variableName); } + } + + public VariableType getPythonVariableType(String variableName) { + checkValidName(variableName); + int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName)); + + VariableType[] values = VariableType.values(); + if (code < 0 || code >= values.length) + return VariableType.UNKNOWN; + + return values[code]; + } + + public String[] getPythonVariableNames() { + return execute(() -> getPythonVariableNamesImpl(contextID)); + } + + private static void checkValidName(String variableName) { + if (!namePattern.matcher(variableName).matches()) + throw new IllegalArgumentException("Invalid Python variable name " + variableName); + } + + static final ExecutorService pythonExecutor = Executors.newSingleThreadExecutor(); + + static void execute(Runnable job) { + try { + pythonExecutor.submit(job).get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException) cause; + else + throw new RuntimeException(cause); + } + } + + static V execute(Callable job) { + try { + return pythonExecutor.submit(job).get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) + throw (RuntimeException) cause; + else + throw new RuntimeException(cause); + } + } + + // Native function declarations + private static native void initializePython(Writer writer); + + private static native long createContextImpl(); + private static native void deleteContextImpl(long contextID); + + private static native int executePythonStatementImpl(long contextID, String statement); + + private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value); + private static native void setPythonLongVariableImpl(long contextID, String variableName, long value); + private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value); + private static native void setPythonStringVariableImpl(long contextID, String variableName, String value); + + private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value); + private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value); + private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value); + private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value); + private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value); + + private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName); + private static native long getPythonLongVariableImpl(long contextID, String variableName); + private static native double getPythonDoubleVariableImpl(long contextID, String variableName); + private static native String getPythonStringVariableImpl(long contextID, String variableName); + + private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName); + private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName); + private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName); + private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName); + private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName); + + private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value); + private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName); + + private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding); + private static native Object getPythonVariantVariableImpl(long contextID, String variableName); + + private static native int getPythonVariableTypeImpl(long contextID, String variableName); + + private static native String[] getPythonVariableNamesImpl(long contextID); +}