From 8885425046e0f89893c0e1ee0fe5c27948dcd2be Mon Sep 17 00:00:00 2001 From: Reino Ruusu Date: Wed, 1 Feb 2017 21:17:18 +0200 Subject: [PATCH] Support for output and exceptions without tracebacks Added a redundant implementation of SCLReportingWriter for backward compatibility. Added support for a user-provided Writer object in executePythonStatement. Moved instantiation of SCLReportingWriter to Java. Added support for sys.stdout.flush() in Python. Change-Id: Icf53ed000844d136b93aa86f7bf41db3559f8748 --- .../jnipython.dll | Bin 66048 -> 35328 bytes .../src/sclpy.c | 161 +++++++----- .../src/sclpy.h | 235 +++++++++--------- .../simantics/pythonlink/PythonContext.java | 90 ++++++- .../pythonlink/SCLReportingWriter.java | 46 ++++ 5 files changed, 340 insertions(+), 192 deletions(-) create mode 100644 org.simantics.pythonlink/src/org/simantics/pythonlink/SCLReportingWriter.java diff --git a/org.simantics.pythonlink.win32.x86_64/jnipython.dll b/org.simantics.pythonlink.win32.x86_64/jnipython.dll index ab792ab08a7ca26dd2a50f39b3abb8bc2f709548..bb0a05aae18c761e217ad82a9f77104dc8cbff9e 100644 GIT binary patch literal 35328 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, WRITER_CLASS); + jmethodID flushMethod = (*env)->GetMethodID(env, writerClass, "flush", "()V"); + + Py_BEGIN_ALLOW_THREADS + (*env)->CallVoidMethod(env, sclWriter, flushMethod); + Py_END_ALLOW_THREADS + } + } + + Py_RETURN_NONE; +} + static PyMethodDef sclWriterMethods[] = { {"write", writeToSCL, METH_VARARGS, "Write something."}, + {"flush", flushSCL, METH_VARARGS, "Flush output."}, {NULL, NULL, 0, NULL} }; +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_initializePython(JNIEnv *env, jobject thisObj, jobject writer) { + Py_Initialize(); -JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) { - char name[16]; + { + static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "sclwriter", NULL, -1, sclWriterMethods, }; + PyObject *m = PyModule_Create(&moduledef); - if (!main_ts) { - Py_Initialize(); + sclWriter = (*env)->NewGlobalRef(env, writer); + + if (m == NULL) throwException(env, PYTHON_EXCEPTION, "Failed to create SCL writer module"); + + PySys_SetObject("stdout", m); + PySys_SetObject("stderr", m); + } + + hasNumpy = _import_array(); + hasNumpy = hasNumpy != -1; - { - static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "sclwriter", NULL, -1, sclWriterMethods, }; - PyObject *m = PyModule_Create(&moduledef); + main_ts = PyEval_SaveThread(); +} - if (m == NULL) throwException(env, PYTHON_EXCEPTION, "Failed to create SCL writer module"); - PySys_SetObject("stdout", m); - PySys_SetObject("stderr", m); - } +JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) { + char name[16]; - hasNumpy = _import_array(); - hasNumpy = hasNumpy != -1; - main_ts = PyEval_SaveThread(); + if (!main_ts) { + return 0; } sprintf(name, "SCL_%d", ++moduleCount); @@ -921,6 +953,37 @@ Java_org_simantics_pythonlink_PythonContext_setPythonVariantVariableImpl( PyEval_SaveThread(); } +static PyObject *getExceptionMessage(PyObject *exceptionType, PyObject *exception, PyObject *traceback) { + PyObject *formatExc = NULL, *args = NULL; + PyObject *tracebackModule = PyImport_ImportModule("traceback"); + if (!tracebackModule) { + return NULL; + } + + if (exception && traceback) { + formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception"); + args = PyTuple_Pack(3, exceptionType, exception, traceback); + } + else if (exception) { + formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception_only"); + args = PyTuple_Pack(2, exceptionType, exception); + } + + Py_DECREF(tracebackModule); + + if (formatExc != NULL && args != NULL) { + PyObject *result = PyObject_CallObject(formatExc, args); + Py_XDECREF(args); + Py_XDECREF(formatExc); + return result; + } + else { + Py_XDECREF(args); + Py_XDECREF(formatExc); + return NULL; + } +} + JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl( JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) { @@ -931,63 +994,38 @@ Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl( { PyObject *module = getModule(contextID); - jclass sclReportingWriterClass = (*env)->FindClass(env, SCL_REPORTING_WRITER_CLASS); - jmethodID constructor = (*env)->GetMethodID(env, sclReportingWriterClass, "", "()V"); - jmethodID flushMethod = (*env)->GetMethodID(env, sclReportingWriterClass, "flush", "()V"); - PyObject *globals; globals = PyModule_GetDict(module); currentEnv = env; - if (sclReportingWriterClass && constructor) - sclWriter = (*env)->NewObject(env, sclReportingWriterClass, constructor); - else - sclWriter = NULL; { PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals); PyObject *exceptionType = PyErr_Occurred(); if (exceptionType != NULL) { - PyObject *exception, *traceback; + PyObject *exception, *traceback, *message; PyErr_Fetch(&exceptionType, &exception, &traceback); - { - PyObject *tracebackModule = PyImport_ImportModule("traceback"); - if (tracebackModule != NULL) { - PyObject *formatExc = PyDict_GetItemString(PyModule_GetDict(tracebackModule), "format_exception"); - if (formatExc != NULL) { - PyObject *args = PyTuple_Pack(3, exceptionType, exception, traceback); - PyObject *message = PyObject_CallObject(formatExc, args); - if (message != NULL) { - PyObject *emptyStr = PyUnicode_FromString(""); - PyObject *joined = PyUnicode_Join(emptyStr, message); - char *messageStr = PyUnicode_AsUTF8(joined); - throwPythonException(env, messageStr); - Py_DECREF(joined); - Py_DECREF(emptyStr); - Py_DECREF(message); - } - else { - PyTypeObject - *ty = (PyTypeObject *)exceptionType; - throwPythonException( - env, ty ? ty->tp_name - : "Internal error, null exception type"); - } - Py_DECREF(args); - Py_DECREF(formatExc); - } - else { - throwPythonException(env, "Internal error, no format_exc function"); - } - Py_DECREF(tracebackModule); - } - else { - throwPythonException(env, "Internal error, no traceback module"); - } + message = getExceptionMessage(exceptionType, exception, traceback); + if (message != NULL) { + PyObject *emptyStr = PyUnicode_FromString(""); + PyObject *joined = PyUnicode_Join(emptyStr, message); + char *messageStr = PyUnicode_AsUTF8(joined); + throwPythonException(env, messageStr); + Py_DECREF(joined); + Py_DECREF(emptyStr); + Py_DECREF(message); + } + else { + PyTypeObject + *ty = (PyTypeObject *)exceptionType; + throwPythonException( + env, ty ? ty->tp_name + : "Internal error, null exception type"); } + Py_XDECREF(exceptionType); Py_XDECREF(exception); Py_XDECREF(traceback); @@ -996,12 +1034,7 @@ Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl( PyEval_SaveThread(); (*env)->ReleaseStringUTFChars(env, statement, utfchars); - if (sclWriter != NULL) { - (*env)->CallVoidMethod(env, sclWriter, flushMethod); - } - currentEnv = NULL; - sclWriter = NULL; return result != NULL ? 0 : 1; } diff --git a/org.simantics.pythonlink.win32.x86_64/src/sclpy.h b/org.simantics.pythonlink.win32.x86_64/src/sclpy.h index 15d681d..e71e107 100644 --- a/org.simantics.pythonlink.win32.x86_64/src/sclpy.h +++ b/org.simantics.pythonlink.win32.x86_64/src/sclpy.h @@ -1,118 +1,117 @@ -#ifndef __MAIN_H__ -#define __MAIN_H__ - -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#ifdef _DEBUG - #undef _DEBUG - #include //header for system python import; add include paths - #include - #define _DEBUG 1 -#else - #include //header for system python import; add include paths - #include -#endif - -#include //java connection header - -/* To use this exported function of dll, include this header - * in your project. - */ - -#ifdef BUILD_DLL - #define DLL_EXPORT __declspec(dllexport) -#else - #define DLL_EXPORT __declspec(dllimport) -#endif - - -#define JAVA_MAXINT (0x7fffffff) - -#define PYTHON_EXCEPTION "org/simantics/pythonlink/PythonException" -#define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException" -#define OBJECT_CLASS "java/lang/Object" -#define STRING_CLASS "java/lang/String" -#define MAP_CLASS "java/util/Map" -#define SET_CLASS "java/util/Set" - -#define WRITER_CLASS "java/io/Writer" -#define SCL_REPORTING_WRITER_CLASS "org/simantics/scl/runtime/reporting/SCLReportingWriter" - -#define PACKAGE_PREFIX "org/simantics/pythonlink/" - -#define NDARRAY_CLASS (PACKAGE_PREFIX "NDArray") - -#define VARIANT_CLASS "org/simantics/databoard/binding/mutable/Variant" -#define BINDINGS_CLASS "org/simantics/databoard/Bindings" -#define BINDING_CLASS "org/simantics/databoard/binding/Binding" -#define DATATYPE_CLASS "org/simantics/databoard/type/Datatype" - -#define BOOLEANTYPE_CLASS "org/simantics/databoard/type/BooleanType" -#define BYTETYPE_CLASS "org/simantics/databoard/type/ByteType" -#define INTEGERTYPE_CLASS "org/simantics/databoard/type/IntegerType" -#define LONGTYPE_CLASS "org/simantics/databoard/type/LongType" -#define FLOATTYPE_CLASS "org/simantics/databoard/type/FloatType" -#define DOUBLETYPE_CLASS "org/simantics/databoard/type/DoubleType" -#define STRINGTYPE_CLASS "org/simantics/databoard/type/StringType" -#define RECORDTYPE_CLASS "org/simantics/databoard/type/RecordType" -#define ARRAYTYPE_CLASS "org/simantics/databoard/type/ArrayType" -#define MAPTYPE_CLASS "org/simantics/databoard/type/MapType" -#define OPTIONALTYPE_CLASS "org/simantics/databoard/type/OptionalType" -#define UNIONTYPE_CLASS "org/simantics/databoard/type/UnionType" -#define VARIANTTYPE_CLASS "org/simantics/databoard/type/VariantType" - -#define BOOLEANBINDING_CLASS "org/simantics/databoard/binding/BooleanBinding" -#define BYTEBINDING_CLASS "org/simantics/databoard/binding/ByteBinding" -#define INTEGERBINDING_CLASS "org/simantics/databoard/binding/IntegerBinding" -#define LONGBINDING_CLASS "org/simantics/databoard/binding/LongBinding" -#define FLOATBINDING_CLASS "org/simantics/databoard/binding/FloatBinding" -#define DOUBLEBINDING_CLASS "org/simantics/databoard/binding/DoubleBinding" -#define STRINGBINDING_CLASS "org/simantics/databoard/binding/StringBinding" -#define RECORDBINDING_CLASS "org/simantics/databoard/binding/RecordBinding" -#define ARRAYBINDING_CLASS "org/simantics/databoard/binding/ArrayBinding" -#define MAPBINDING_CLASS "org/simantics/databoard/binding/MapBinding" -#define OPTIONALBINDING_CLASS "org/simantics/databoard/binding/OptionalBinding" -#define UNIONBINDING_CLASS "org/simantics/databoard/binding/UnionBinding" -#define VARIANTBINDING_CLASS "org/simantics/databoard/binding/VariantBinding" - -#define COMPONENT_CLASS "org/simantics/databoard/type/Component" -#define TAGGEDOBJECT_CLASS "org/simantics/databoard/binding/mutable/TaggedObject" - -PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value); -PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value); -PyObject *getPythonIntegerList(JNIEnv *env, jintArray value); -PyObject *getPythonLongList(JNIEnv *env, jlongArray value); -PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value); -PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value); - -PyObject *getPythonObject(JNIEnv *env, jobject object, jobject binding); - -PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonRecordObject(JNIEnv *env, jobjectArray object, jobject binding); -PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding); -PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding); - -void setPythonVariable(PyObject *module, PyObject *name, PyObject *value); - -jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value); -jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value); -jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value); -jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value); -jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string); -jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq); -jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *list); -jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *list); -jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *list); -jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *list); -jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict); -jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array); -jobject pythonObjectAsObject(JNIEnv *env, PyObject *value); - -#endif // __MAIN_H__ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#ifdef _DEBUG + #undef _DEBUG + #include //header for system python import; add include paths + #include + #define _DEBUG 1 +#else + #include //header for system python import; add include paths + #include +#endif + +#include //java connection header + +/* To use this exported function of dll, include this header + * in your project. + */ + +#ifdef BUILD_DLL + #define DLL_EXPORT __declspec(dllexport) +#else + #define DLL_EXPORT __declspec(dllimport) +#endif + + +#define JAVA_MAXINT (0x7fffffff) + +#define PYTHON_EXCEPTION "org/simantics/pythonlink/PythonException" +#define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException" +#define OBJECT_CLASS "java/lang/Object" +#define STRING_CLASS "java/lang/String" +#define MAP_CLASS "java/util/Map" +#define SET_CLASS "java/util/Set" + +#define WRITER_CLASS "java/io/Writer" + +#define PACKAGE_PREFIX "org/simantics/pythonlink/" + +#define NDARRAY_CLASS (PACKAGE_PREFIX "NDArray") + +#define VARIANT_CLASS "org/simantics/databoard/binding/mutable/Variant" +#define BINDINGS_CLASS "org/simantics/databoard/Bindings" +#define BINDING_CLASS "org/simantics/databoard/binding/Binding" +#define DATATYPE_CLASS "org/simantics/databoard/type/Datatype" + +#define BOOLEANTYPE_CLASS "org/simantics/databoard/type/BooleanType" +#define BYTETYPE_CLASS "org/simantics/databoard/type/ByteType" +#define INTEGERTYPE_CLASS "org/simantics/databoard/type/IntegerType" +#define LONGTYPE_CLASS "org/simantics/databoard/type/LongType" +#define FLOATTYPE_CLASS "org/simantics/databoard/type/FloatType" +#define DOUBLETYPE_CLASS "org/simantics/databoard/type/DoubleType" +#define STRINGTYPE_CLASS "org/simantics/databoard/type/StringType" +#define RECORDTYPE_CLASS "org/simantics/databoard/type/RecordType" +#define ARRAYTYPE_CLASS "org/simantics/databoard/type/ArrayType" +#define MAPTYPE_CLASS "org/simantics/databoard/type/MapType" +#define OPTIONALTYPE_CLASS "org/simantics/databoard/type/OptionalType" +#define UNIONTYPE_CLASS "org/simantics/databoard/type/UnionType" +#define VARIANTTYPE_CLASS "org/simantics/databoard/type/VariantType" + +#define BOOLEANBINDING_CLASS "org/simantics/databoard/binding/BooleanBinding" +#define BYTEBINDING_CLASS "org/simantics/databoard/binding/ByteBinding" +#define INTEGERBINDING_CLASS "org/simantics/databoard/binding/IntegerBinding" +#define LONGBINDING_CLASS "org/simantics/databoard/binding/LongBinding" +#define FLOATBINDING_CLASS "org/simantics/databoard/binding/FloatBinding" +#define DOUBLEBINDING_CLASS "org/simantics/databoard/binding/DoubleBinding" +#define STRINGBINDING_CLASS "org/simantics/databoard/binding/StringBinding" +#define RECORDBINDING_CLASS "org/simantics/databoard/binding/RecordBinding" +#define ARRAYBINDING_CLASS "org/simantics/databoard/binding/ArrayBinding" +#define MAPBINDING_CLASS "org/simantics/databoard/binding/MapBinding" +#define OPTIONALBINDING_CLASS "org/simantics/databoard/binding/OptionalBinding" +#define UNIONBINDING_CLASS "org/simantics/databoard/binding/UnionBinding" +#define VARIANTBINDING_CLASS "org/simantics/databoard/binding/VariantBinding" + +#define COMPONENT_CLASS "org/simantics/databoard/type/Component" +#define TAGGEDOBJECT_CLASS "org/simantics/databoard/binding/mutable/TaggedObject" + +PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value); +PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value); +PyObject *getPythonIntegerList(JNIEnv *env, jintArray value); +PyObject *getPythonLongList(JNIEnv *env, jlongArray value); +PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value); +PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value); + +PyObject *getPythonObject(JNIEnv *env, jobject object, jobject binding); + +PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonRecordObject(JNIEnv *env, jobjectArray object, jobject binding); +PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding); +PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding); + +void setPythonVariable(PyObject *module, PyObject *name, PyObject *value); + +jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value); +jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value); +jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value); +jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value); +jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string); +jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq); +jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *list); +jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *list); +jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *list); +jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *list); +jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict); +jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array); +jobject pythonObjectAsObject(JNIEnv *env, PyObject *value); + +#endif // __MAIN_H__ 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); diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/SCLReportingWriter.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/SCLReportingWriter.java new file mode 100644 index 0000000..0bab712 --- /dev/null +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/SCLReportingWriter.java @@ -0,0 +1,46 @@ +package org.simantics.pythonlink; + +import java.io.Writer; + +import org.simantics.scl.runtime.reporting.SCLReporting; + +public class SCLReportingWriter extends Writer { + + StringBuilder builder = null; + + public SCLReportingWriter() { + builder = new StringBuilder(); + } + + @Override + public void close() { + flush(); + } + + @Override + public void flush() { + if (builder.length() > 0) { + SCLReporting.print(builder.toString()); + builder.setLength(0); + } + } + + @Override + public void write(char[] buf, int off, int len) { + if (len == 0) return; + + if (len < 0) throw new IllegalArgumentException("Negative buffer region length"); + if (off < 0) throw new IllegalArgumentException("Negative buffer region offset"); + if (off + len > buf.length) throw new IllegalArgumentException("Buffer region overflow"); + + for (int i = 0; i < len; i++) { + if (buf[off + i] == '\n') { + SCLReporting.print(builder.toString()); + builder.setLength(0); + } + else { + builder.append(buf[off + i]); + } + } + } +} -- 2.47.1