]> gerrit.simantics Code Review - simantics/python.git/blobdiff - org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java
Report and log failure of Python DLL loading
[simantics/python.git] / org.simantics.pythonlink / src / org / simantics / pythonlink / PythonContext.java
index 6c96dd3bde8a877e1e2115c4f1686f4101a44200..77701ef5b352423e18d66ae90790dc296c178091 100644 (file)
-package org.simantics.pythonlink;\r
-\r
-import java.io.Closeable;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-import java.util.concurrent.Callable;\r
-import java.util.concurrent.ExecutionException;\r
-import java.util.concurrent.ExecutorService;\r
-import java.util.concurrent.Executors;\r
-import java.util.regex.Pattern;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.error.BindingException;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-\r
-public class PythonContext implements Closeable {\r
-    private long contextID;\r
-    \r
-    public interface Listener {\r
-       void updated(String variableName);\r
-       void closed();\r
-    }\r
-    \r
-    static ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();\r
-    \r
-    Set<Listener> listeners = new HashSet<>();\r
-    \r
-    public enum VariableType {\r
-       NO_VARIABLE,\r
-       BOOLEAN,\r
-       LONG,\r
-       FLOAT,\r
-       STRING,\r
-       BYTEARRAY,\r
-       DICTIONARY,\r
-       NDARRAY,\r
-       SEQUENCE,\r
-       UNKNOWN\r
-    }\r
-    \r
-    static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)");\r
-    \r
-    PythonContext() {\r
-        contextID = createContextImpl();\r
-    }\r
-    \r
-    public void addListener(Listener listener) {\r
-       listeners.add(listener);\r
-    }\r
-    \r
-    public void removeListener(Listener listener) {\r
-               listeners.remove(listener);\r
-    }\r
-    \r
-    @Override\r
-    public void close() {\r
-        long id = contextID;\r
-        contextID = 0;\r
-        if (id != 0) deleteContextImpl(id);\r
-        \r
-       for (Listener l : listeners) {\r
-               l.closed();\r
-       }\r
-    }\r
-\r
-       public boolean isOpen() {\r
-               return contextID != 0;\r
-       }\r
-       \r
-    @Override\r
-    protected void finalize() throws Throwable {\r
-        super.finalize();\r
-        close();\r
-    }\r
-    \r
-    public void executePythonStatement(String statement) {\r
-        execute(() -> executePythonStatementImpl( contextID, statement ));\r
-       for (Listener l : listeners) { l.updated(null); }\r
-    }\r
-\r
-    // Setters\r
-    \r
-    public void setPythonBooleanVariable(String variableName, boolean value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-\r
-    public void setPythonIntegerVariable(String variableName, int value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonLongVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonLongVariable(String variableName, long value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonLongVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonDoubleVariable(String variableName, double value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonStringVariable(String variableName, String value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonStringVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    \r
-    public void setPythonBooleanArrayVariable(String variableName, boolean[] value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonIntegerArrayVariable(String variableName, int[] value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonLongArrayVariable(String variableName, long[] value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonDoubleArrayVariable(String variableName, double[] value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    public void setPythonStringArrayVariable(String variableName, String[] value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value));\r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-\r
-    // Getters\r
-    \r
-    public boolean getPythonBooleanVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return getPythonBooleanVariableImpl(contextID, variableName);\r
-    }\r
-    public int getPythonIntegerVariable(String variableName) {\r
-        checkValidName(variableName);\r
-       long value = execute(() -> getPythonLongVariableImpl(contextID, variableName));\r
-       if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE)\r
-               throw new RuntimeException("Python value not in integer range");\r
-        return (int) value;\r
-    }\r
-    public long getPythonLongVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonLongVariableImpl(contextID, variableName));\r
-    }\r
-    public double getPythonDoubleVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonDoubleVariableImpl(contextID, variableName));\r
-    }\r
-    public String getPythonStringVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonStringVariableImpl(contextID, variableName));\r
-    }\r
-    \r
-    public boolean[] getPythonBooleanArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName));\r
-    }\r
-    public int[] getPythonIntegerArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName));\r
-    }\r
-    public long[] getPythonLongArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName));\r
-    }\r
-    public double[] getPythonDoubleArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName));\r
-    }\r
-    public String[] getPythonStringArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName));\r
-    }\r
-    \r
-    public void setPythonNDArrayVariable(String variableName, NDArray value) {\r
-        checkValidName(variableName);\r
-        execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value));\r
-    }\r
-    public NDArray getPythonNDArrayVariable(String variableName) {\r
-        checkValidName(variableName);\r
-        return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName));\r
-    }\r
-\r
-    public Object getPythonVariantVariable(String variableName, Binding binding) {\r
-        checkValidName(variableName);\r
-       Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName));\r
-       try {\r
-                       return Bindings.OBJECT.getContent(result, binding);\r
-               } catch (BindingException e) {\r
-                       throw new RuntimeException(e);\r
-               }\r
-    }\r
-    \r
-    public Variant getPythonVariantVariable(String variableName) {\r
-        checkValidName(variableName);\r
-       return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName)));\r
-    }\r
-\r
-    public void setPythonVariantVariable(String variableName, Variant value) {\r
-       setPythonVariantVariable(variableName, value.getValue(), value.getBinding());\r
-    }\r
-    \r
-    public void setPythonVariantVariable(String variableName, Object value, Binding binding) {\r
-        checkValidName(variableName);\r
-        if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding");\r
-        \r
-        execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding));\r
-        \r
-       for (Listener l : listeners) { l.updated(variableName); }\r
-    }\r
-    \r
-    public VariableType getPythonVariableType(String variableName) {\r
-        checkValidName(variableName);\r
-       int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName));\r
-       \r
-       VariableType[] values = VariableType.values();\r
-       if (code < 0 || code >= values.length)\r
-               return VariableType.UNKNOWN;\r
-       \r
-       return values[code];\r
-    }\r
-    \r
-    public String[] getPythonVariableNames() {\r
-       return execute(() -> getPythonVariableNamesImpl(contextID));\r
-    }\r
-     \r
-    private static void checkValidName(String variableName) {\r
-        if (!namePattern.matcher(variableName).matches())\r
-            throw new IllegalArgumentException("Invalid Python variable name " + variableName);\r
-    }\r
-    \r
-    static void execute(Runnable job) {\r
-        try {\r
-            pythonExecutor.submit(job).get();\r
-        } catch (InterruptedException | ExecutionException e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-    }\r
-    \r
-    static <V> V execute(Callable<V> job) {\r
-        try {\r
-            return pythonExecutor.submit(job).get();\r
-        } catch (InterruptedException | ExecutionException e) {\r
-            throw new RuntimeException(e);\r
-        }\r
-    }\r
-    \r
-    // Native function declarations\r
-    private static native long createContextImpl();\r
-    private static native void deleteContextImpl(long contextID);\r
-    \r
-    private static native int executePythonStatementImpl(long contextID, String statement);\r
-    \r
-    private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value);\r
-    private static native void setPythonLongVariableImpl(long contextID, String variableName, long value);\r
-    private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value);\r
-    private static native void setPythonStringVariableImpl(long contextID, String variableName, String value);\r
-    \r
-    private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value);\r
-    private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value);\r
-    private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value);\r
-    private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value);\r
-    private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value);\r
-    \r
-    private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName);\r
-    private static native long getPythonLongVariableImpl(long contextID, String variableName);\r
-    private static native double getPythonDoubleVariableImpl(long contextID, String variableName);\r
-    private static native String getPythonStringVariableImpl(long contextID, String variableName);\r
-    \r
-    private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName);\r
-    private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName);\r
-    private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName);\r
-    private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName);\r
-    private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName);\r
-    \r
-    private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value);\r
-    private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName);\r
-    \r
-    private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding);\r
-    private static native Object getPythonVariantVariableImpl(long contextID, String variableName);\r
-    \r
-    private static native int getPythonVariableTypeImpl(long contextID, String variableName);\r
-    \r
-    private static native String[] getPythonVariableNamesImpl(long contextID);\r
-}\r
+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<Listener> 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> V execute(Callable<V> 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);
+}