]> gerrit.simantics Code Review - simantics/python.git/blob - 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
1 package org.simantics.pythonlink;
2
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.io.OutputStreamWriter;
6 import java.io.Writer;
7 import java.util.HashSet;
8 import java.util.Set;
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;
14
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;
20
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;
25         
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);
29         
30                 @Override
31                 public void close() throws IOException {
32                         throw new IllegalStateException("This writer should never be closed!");
33                 }
34
35                 @Override
36                 public void flush() throws IOException {
37                 Writer writer = getPythonWriter();
38                 synchronized (writer) {
39                         writer.flush();
40                 }
41                 }
42
43                 @Override
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);
48                 }
49                 }
50                 
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;
56                 }
57     };
58     
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.");
62         }
63         
64                 if (!isPyInitialized) {
65                         execute(() -> initializePython(pythonWriter));
66                         isPyInitialized = true;
67                 }
68         }
69
70         public interface Listener {
71         void updated(String variableName);
72         void closed();
73     }
74     
75     Set<Listener> listeners = new HashSet<>();
76     
77     public enum VariableType {
78         NO_VARIABLE,
79         BOOLEAN,
80         LONG,
81         FLOAT,
82         STRING,
83         BYTEARRAY,
84         DICTIONARY,
85         NDARRAY,
86         SEQUENCE,
87         UNKNOWN
88     }
89     
90     static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)");
91     
92     // Really a C pointer.
93     private long contextID;
94
95         PythonContext() {
96                 ensurePythonInit();
97         contextID = execute(() -> createContextImpl());
98         if (contextID == 0) {
99                 throw new PythonException("Python initialization has failed");
100         }
101     }
102     
103     public void addListener(Listener listener) {
104         listeners.add(listener);
105     }
106     
107     public void removeListener(Listener listener) {
108                 listeners.remove(listener);
109     }
110     
111     @Override
112     public synchronized void close() {
113         long id = contextID;
114         contextID = 0;
115         if (id != 0) execute(() -> deleteContextImpl(id));
116         
117         for (Listener l : listeners) {
118                 l.closed();
119         }
120     }
121
122         public boolean isOpen() {
123                 return contextID != 0;
124         }
125         
126     @Override
127     protected void finalize() throws Throwable {
128         super.finalize();
129         close();
130     }
131     
132     public void executePythonStatement(String statement, Writer writer) {
133         SCLContext sclContext = SCLContext.getCurrent();
134         
135         execute(() -> {
136                 SCLContext.push(sclContext);
137                 Writer oldWriter = (Writer) sclContext.put(PYTHON_SCL_WRITER, writer);
138                 try {
139                         executePythonStatementImpl( contextID, statement );
140                         pythonWriter.flush();
141                         } catch (IOException e) {
142                                 e.printStackTrace();
143                         } finally {
144                                 if (oldWriter != null) {
145                                         sclContext.put(PYTHON_SCL_WRITER, oldWriter);
146                                 }
147                                 else {
148                                         sclContext.remove(PYTHON_SCL_WRITER);
149                                 }
150                 }
151                 SCLContext.pop();
152         });
153         
154         for (Listener l : listeners) { l.updated(null); }
155     }
156
157     public void executePythonStatement(String statement) {
158         executePythonStatement(statement, new SCLReportingWriter());
159     }
160
161     // Setters
162     
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); }
167     }
168
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); }
173     }
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); }
178     }
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); }
183     }
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); }
188     }
189     
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); }
194     }
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); }
199     }
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); }
204     }
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); }
209     }
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); }
214     }
215
216     // Getters
217     
218     public boolean getPythonBooleanVariable(String variableName) {
219         checkValidName(variableName);
220         return getPythonBooleanVariableImpl(contextID, variableName);
221     }
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");
227         return (int) value;
228     }
229     public long getPythonLongVariable(String variableName) {
230         checkValidName(variableName);
231         return execute(() -> getPythonLongVariableImpl(contextID, variableName));
232     }
233     public double getPythonDoubleVariable(String variableName) {
234         checkValidName(variableName);
235         return execute(() -> getPythonDoubleVariableImpl(contextID, variableName));
236     }
237     public String getPythonStringVariable(String variableName) {
238         checkValidName(variableName);
239         return execute(() -> getPythonStringVariableImpl(contextID, variableName));
240     }
241     
242     public boolean[] getPythonBooleanArrayVariable(String variableName) {
243         checkValidName(variableName);
244         return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName));
245     }
246     public int[] getPythonIntegerArrayVariable(String variableName) {
247         checkValidName(variableName);
248         return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName));
249     }
250     public long[] getPythonLongArrayVariable(String variableName) {
251         checkValidName(variableName);
252         return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName));
253     }
254     public double[] getPythonDoubleArrayVariable(String variableName) {
255         checkValidName(variableName);
256         return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName));
257     }
258     public String[] getPythonStringArrayVariable(String variableName) {
259         checkValidName(variableName);
260         return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName));
261     }
262     
263     public void setPythonNDArrayVariable(String variableName, NDArray value) {
264         checkValidName(variableName);
265         execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value));
266     }
267     public NDArray getPythonNDArrayVariable(String variableName) {
268         checkValidName(variableName);
269         return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName));
270     }
271
272     public Object getPythonVariantVariable(String variableName, Binding binding) {
273         checkValidName(variableName);
274         Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName));
275         try {
276                         return Bindings.OBJECT.getContent(result, binding);
277                 } catch (BindingException e) {
278                         throw new RuntimeException(e);
279                 }
280     }
281     
282     public Variant getPythonVariantVariable(String variableName) {
283         checkValidName(variableName);
284         return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName)));
285     }
286
287     public void setPythonVariantVariable(String variableName, Variant value) {
288         setPythonVariantVariable(variableName, value.getValue(), value.getBinding());
289     }
290     
291     public void setPythonVariantVariable(String variableName, Object value, Binding binding) {
292         checkValidName(variableName);
293         if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding");
294         
295         execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding));
296         
297         for (Listener l : listeners) { l.updated(variableName); }
298     }
299     
300     public VariableType getPythonVariableType(String variableName) {
301         checkValidName(variableName);
302         int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName));
303         
304         VariableType[] values = VariableType.values();
305         if (code < 0 || code >= values.length)
306                 return VariableType.UNKNOWN;
307         
308         return values[code];
309     }
310     
311     public String[] getPythonVariableNames() {
312         return execute(() -> getPythonVariableNamesImpl(contextID));
313     }
314      
315     private static void checkValidName(String variableName) {
316         if (!namePattern.matcher(variableName).matches())
317             throw new IllegalArgumentException("Invalid Python variable name " + variableName);
318     }
319     
320     static final ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();
321
322         static void execute(Runnable job) {
323         try {
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;
331             else
332                 throw new RuntimeException(cause);
333         }
334     }
335     
336     static <V> V execute(Callable<V> job) {
337         try {
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;
345             else
346                 throw new RuntimeException(cause);
347         }
348     }
349     
350     // Native function declarations
351     private static native void initializePython(Writer writer);
352     
353     private static native long createContextImpl();
354     private static native void deleteContextImpl(long contextID);
355     
356     private static native int executePythonStatementImpl(long contextID, String statement);
357     
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);
362     
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);
368     
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);
373     
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);
379     
380     private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value);
381     private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName);
382     
383     private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding);
384     private static native Object getPythonVariantVariableImpl(long contextID, String variableName);
385     
386     private static native int getPythonVariableTypeImpl(long contextID, String variableName);
387     
388     private static native String[] getPythonVariableNamesImpl(long contextID);
389 }