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 org.simantics.databoard.binding.error.BindingException;
import org.simantics.databoard.binding.mutable.Variant;
import org.simantics.scl.runtime.SCLContext;
-import org.simantics.scl.runtime.reporting.SCLReportingHandler;
public class PythonContext implements Closeable {
- private long contextID;
+ 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;
+ }
+ };
- public interface Listener {
+ private static synchronized void ensurePythonInit() {
+ if (!isPyInitialized) {
+ execute(() -> initializePython(pythonWriter));
+ isPyInitialized = true;
+ }
+ }
+
+ public interface Listener {
void updated(String variableName);
void closed();
}
- static ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();
-
Set<Listener> listeners = new HashSet<>();
public enum VariableType {
static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)");
- PythonContext() {
+ // 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) {
}
@Override
- public void close() {
+ public synchronized void close() {
long id = contextID;
contextID = 0;
if (id != 0) execute(() -> deleteContextImpl(id));
close();
}
- public void executePythonStatement(String statement) {
+ public void executePythonStatement(String statement, Writer writer) {
SCLContext sclContext = SCLContext.getCurrent();
execute(() -> {
SCLContext.push(sclContext);
- executePythonStatementImpl( contextID, statement );
+ 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) {
throw new IllegalArgumentException("Invalid Python variable name " + variableName);
}
- static void execute(Runnable job) {
+ static final ExecutorService pythonExecutor = Executors.newSingleThreadExecutor();
+
+ static void execute(Runnable job) {
try {
pythonExecutor.submit(job).get();
} catch (InterruptedException | ExecutionException e) {
}
// Native function declarations
+ private static native void initializePython(Writer writer);
+
private static native long createContextImpl();
private static native void deleteContextImpl(long contextID);