X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.pythonlink%2Fsrc%2Forg%2Fsimantics%2Fpythonlink%2FPythonContext.java;h=77701ef5b352423e18d66ae90790dc296c178091;hb=refs%2Fheads%2Frelease%2F1.35.2;hp=ecf925d9bfc9c358a72e5b5303b6bbf315f1a7b4;hpb=c09eb1d21fa19556b14301ca70b622b8a2e1d080;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 ecf925d..77701ef 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; @@ -13,17 +16,62 @@ 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 { - 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 (!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(); } - static ExecutorService pythonExecutor = Executors.newSingleThreadExecutor(); - Set listeners = new HashSet<>(); public enum VariableType { @@ -41,8 +89,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) { @@ -54,7 +109,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)); @@ -74,11 +129,35 @@ public class PythonContext implements Closeable { close(); } - public void executePythonStatement(String statement) { - execute(() -> executePythonStatementImpl( contextID, statement )); + 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) { @@ -238,23 +317,39 @@ 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) { + } 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 execute(Callable job) { try { return pythonExecutor.submit(job).get(); - } catch (InterruptedException | ExecutionException e) { + } 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);