X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.pythonlink%2Fsrc%2Forg%2Fsimantics%2Fpythonlink%2FPythonContext.java;fp=org.simantics.pythonlink%2Fsrc%2Forg%2Fsimantics%2Fpythonlink%2FPythonContext.java;h=62703f0795f0d80c6b915cf5c4022d7cfa3c71cd;hb=8885425046e0f89893c0e1ee0fe5c27948dcd2be;hp=16b02348196b4326f70f4475173a94677928c433;hpb=1ea9a623d145875880b543e8eea62e801561fba9;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 16b0234..62703f0 100644 --- a/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java @@ -1,6 +1,9 @@ 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; @@ -14,18 +17,57 @@ 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; -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 listeners = new HashSet<>(); public enum VariableType { @@ -43,8 +85,15 @@ public class PythonContext implements Closeable { 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) { @@ -56,7 +105,7 @@ public class PythonContext implements Closeable { } @Override - public void close() { + public synchronized void close() { long id = contextID; contextID = 0; if (id != 0) execute(() -> deleteContextImpl(id)); @@ -76,18 +125,35 @@ public class PythonContext implements Closeable { 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) { @@ -247,7 +313,9 @@ public class PythonContext implements Closeable { 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) { @@ -264,6 +332,8 @@ public class PythonContext implements Closeable { } // Native function declarations + private static native void initializePython(Writer writer); + private static native long createContextImpl(); private static native void deleteContextImpl(long contextID);