]> gerrit.simantics Code Review - simantics/python.git/blob - org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java
cceace3c4d1b2bf9b372eeb4182d50eede2ba794
[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 (!isPyInitialized) {
61                         execute(() -> initializePython(pythonWriter));
62                         isPyInitialized = true;
63                 }
64         }
65
66         public interface Listener {
67         void updated(String variableName);
68         void closed();
69     }
70     
71     Set<Listener> listeners = new HashSet<>();
72     
73     public enum VariableType {
74         NO_VARIABLE,
75         BOOLEAN,
76         LONG,
77         FLOAT,
78         STRING,
79         BYTEARRAY,
80         DICTIONARY,
81         NDARRAY,
82         SEQUENCE,
83         UNKNOWN
84     }
85     
86     static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)");
87     
88     // Really a C pointer.
89     private long contextID;
90
91         PythonContext() {
92                 ensurePythonInit();
93         contextID = execute(() -> createContextImpl());
94         if (contextID == 0) {
95                 throw new PythonException("Python initialization has failed");
96         }
97     }
98     
99     public void addListener(Listener listener) {
100         listeners.add(listener);
101     }
102     
103     public void removeListener(Listener listener) {
104                 listeners.remove(listener);
105     }
106     
107     @Override
108     public synchronized void close() {
109         long id = contextID;
110         contextID = 0;
111         if (id != 0) execute(() -> deleteContextImpl(id));
112         
113         for (Listener l : listeners) {
114                 l.closed();
115         }
116     }
117
118         public boolean isOpen() {
119                 return contextID != 0;
120         }
121         
122     @Override
123     protected void finalize() throws Throwable {
124         super.finalize();
125         close();
126     }
127     
128     public void executePythonStatement(String statement, Writer writer) {
129         SCLContext sclContext = SCLContext.getCurrent();
130         
131         execute(() -> {
132                 SCLContext.push(sclContext);
133                 Writer oldWriter = (Writer) sclContext.put(PYTHON_SCL_WRITER, writer);
134                 try {
135                         executePythonStatementImpl( contextID, statement );
136                         pythonWriter.flush();
137                         } catch (IOException e) {
138                                 e.printStackTrace();
139                         } finally {
140                                 if (oldWriter != null) {
141                                         sclContext.put(PYTHON_SCL_WRITER, oldWriter);
142                                 }
143                                 else {
144                                         sclContext.remove(PYTHON_SCL_WRITER);
145                                 }
146                 }
147                 SCLContext.pop();
148         });
149         
150         for (Listener l : listeners) { l.updated(null); }
151     }
152
153     public void executePythonStatement(String statement) {
154         executePythonStatement(statement, new SCLReportingWriter());
155     }
156
157     // Setters
158     
159     public void setPythonBooleanVariable(String variableName, boolean value) {
160         checkValidName(variableName);
161         execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value));
162         for (Listener l : listeners) { l.updated(variableName); }
163     }
164
165     public void setPythonIntegerVariable(String variableName, int value) {
166         checkValidName(variableName);
167         execute(() -> setPythonLongVariableImpl(contextID, variableName, value));
168         for (Listener l : listeners) { l.updated(variableName); }
169     }
170     public void setPythonLongVariable(String variableName, long value) {
171         checkValidName(variableName);
172         execute(() -> setPythonLongVariableImpl(contextID, variableName, value));
173         for (Listener l : listeners) { l.updated(variableName); }
174     }
175     public void setPythonDoubleVariable(String variableName, double value) {
176         checkValidName(variableName);
177         execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value));
178         for (Listener l : listeners) { l.updated(variableName); }
179     }
180     public void setPythonStringVariable(String variableName, String value) {
181         checkValidName(variableName);
182         execute(() -> setPythonStringVariableImpl(contextID, variableName, value));
183         for (Listener l : listeners) { l.updated(variableName); }
184     }
185     
186     public void setPythonBooleanArrayVariable(String variableName, boolean[] value) {
187         checkValidName(variableName);
188         execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value));
189         for (Listener l : listeners) { l.updated(variableName); }
190     }
191     public void setPythonIntegerArrayVariable(String variableName, int[] value) {
192         checkValidName(variableName);
193         execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value));
194         for (Listener l : listeners) { l.updated(variableName); }
195     }
196     public void setPythonLongArrayVariable(String variableName, long[] value) {
197         checkValidName(variableName);
198         execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value));
199         for (Listener l : listeners) { l.updated(variableName); }
200     }
201     public void setPythonDoubleArrayVariable(String variableName, double[] value) {
202         checkValidName(variableName);
203         execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value));
204         for (Listener l : listeners) { l.updated(variableName); }
205     }
206     public void setPythonStringArrayVariable(String variableName, String[] value) {
207         checkValidName(variableName);
208         execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value));
209         for (Listener l : listeners) { l.updated(variableName); }
210     }
211
212     // Getters
213     
214     public boolean getPythonBooleanVariable(String variableName) {
215         checkValidName(variableName);
216         return getPythonBooleanVariableImpl(contextID, variableName);
217     }
218     public int getPythonIntegerVariable(String variableName) {
219         checkValidName(variableName);
220         long value = execute(() -> getPythonLongVariableImpl(contextID, variableName));
221         if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE)
222                 throw new RuntimeException("Python value not in integer range");
223         return (int) value;
224     }
225     public long getPythonLongVariable(String variableName) {
226         checkValidName(variableName);
227         return execute(() -> getPythonLongVariableImpl(contextID, variableName));
228     }
229     public double getPythonDoubleVariable(String variableName) {
230         checkValidName(variableName);
231         return execute(() -> getPythonDoubleVariableImpl(contextID, variableName));
232     }
233     public String getPythonStringVariable(String variableName) {
234         checkValidName(variableName);
235         return execute(() -> getPythonStringVariableImpl(contextID, variableName));
236     }
237     
238     public boolean[] getPythonBooleanArrayVariable(String variableName) {
239         checkValidName(variableName);
240         return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName));
241     }
242     public int[] getPythonIntegerArrayVariable(String variableName) {
243         checkValidName(variableName);
244         return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName));
245     }
246     public long[] getPythonLongArrayVariable(String variableName) {
247         checkValidName(variableName);
248         return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName));
249     }
250     public double[] getPythonDoubleArrayVariable(String variableName) {
251         checkValidName(variableName);
252         return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName));
253     }
254     public String[] getPythonStringArrayVariable(String variableName) {
255         checkValidName(variableName);
256         return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName));
257     }
258     
259     public void setPythonNDArrayVariable(String variableName, NDArray value) {
260         checkValidName(variableName);
261         execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value));
262     }
263     public NDArray getPythonNDArrayVariable(String variableName) {
264         checkValidName(variableName);
265         return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName));
266     }
267
268     public Object getPythonVariantVariable(String variableName, Binding binding) {
269         checkValidName(variableName);
270         Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName));
271         try {
272                         return Bindings.OBJECT.getContent(result, binding);
273                 } catch (BindingException e) {
274                         throw new RuntimeException(e);
275                 }
276     }
277     
278     public Variant getPythonVariantVariable(String variableName) {
279         checkValidName(variableName);
280         return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName)));
281     }
282
283     public void setPythonVariantVariable(String variableName, Variant value) {
284         setPythonVariantVariable(variableName, value.getValue(), value.getBinding());
285     }
286     
287     public void setPythonVariantVariable(String variableName, Object value, Binding binding) {
288         checkValidName(variableName);
289         if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding");
290         
291         execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding));
292         
293         for (Listener l : listeners) { l.updated(variableName); }
294     }
295     
296     public VariableType getPythonVariableType(String variableName) {
297         checkValidName(variableName);
298         int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName));
299         
300         VariableType[] values = VariableType.values();
301         if (code < 0 || code >= values.length)
302                 return VariableType.UNKNOWN;
303         
304         return values[code];
305     }
306     
307     public String[] getPythonVariableNames() {
308         return execute(() -> getPythonVariableNamesImpl(contextID));
309     }
310      
311     private static void checkValidName(String variableName) {
312         if (!namePattern.matcher(variableName).matches())
313             throw new IllegalArgumentException("Invalid Python variable name " + variableName);
314     }
315     
316     static final ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();
317
318         static void execute(Runnable job) {
319         try {
320             pythonExecutor.submit(job).get();
321         } catch (InterruptedException e) {
322             throw new RuntimeException(e);
323         } catch (ExecutionException e) {
324             Throwable cause = e.getCause();
325             if (cause instanceof RuntimeException)
326                 throw (RuntimeException) cause;
327             else
328                 throw new RuntimeException(cause);
329         }
330     }
331     
332     static <V> V execute(Callable<V> job) {
333         try {
334             return pythonExecutor.submit(job).get();
335         } catch (InterruptedException e) {
336             throw new RuntimeException(e);
337         } catch (ExecutionException e) {
338             Throwable cause = e.getCause();
339             if (cause instanceof RuntimeException)
340                 throw (RuntimeException) cause;
341             else
342                 throw new RuntimeException(cause);
343         }
344     }
345     
346     // Native function declarations
347     private static native void initializePython(Writer writer);
348     
349     private static native long createContextImpl();
350     private static native void deleteContextImpl(long contextID);
351     
352     private static native int executePythonStatementImpl(long contextID, String statement);
353     
354     private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value);
355     private static native void setPythonLongVariableImpl(long contextID, String variableName, long value);
356     private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value);
357     private static native void setPythonStringVariableImpl(long contextID, String variableName, String value);
358     
359     private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value);
360     private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value);
361     private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value);
362     private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value);
363     private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value);
364     
365     private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName);
366     private static native long getPythonLongVariableImpl(long contextID, String variableName);
367     private static native double getPythonDoubleVariableImpl(long contextID, String variableName);
368     private static native String getPythonStringVariableImpl(long contextID, String variableName);
369     
370     private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName);
371     private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName);
372     private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName);
373     private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName);
374     private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName);
375     
376     private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value);
377     private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName);
378     
379     private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding);
380     private static native Object getPythonVariantVariableImpl(long contextID, String variableName);
381     
382     private static native int getPythonVariableTypeImpl(long contextID, String variableName);
383     
384     private static native String[] getPythonVariableNamesImpl(long contextID);
385 }