]> gerrit.simantics Code Review - simantics/python.git/commitdiff
Support for output and exceptions without tracebacks 19/319/3
authorReino Ruusu <reino.ruusu@vtt.fi>
Wed, 1 Feb 2017 19:17:18 +0000 (21:17 +0200)
committertkorvola <Timo.Korvola@vtt.fi>
Mon, 6 Feb 2017 11:42:13 +0000 (13:42 +0200)
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

org.simantics.pythonlink.win32.x86_64/jnipython.dll
org.simantics.pythonlink.win32.x86_64/src/sclpy.c
org.simantics.pythonlink.win32.x86_64/src/sclpy.h
org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java
org.simantics.pythonlink/src/org/simantics/pythonlink/SCLReportingWriter.java [new file with mode: 0644]

index ab792ab08a7ca26dd2a50f39b3abb8bc2f709548..bb0a05aae18c761e217ad82a9f77104dc8cbff9e 100644 (file)
Binary files a/org.simantics.pythonlink.win32.x86_64/jnipython.dll and b/org.simantics.pythonlink.win32.x86_64/jnipython.dll differ
index 428a45a35667fe34fcf70b0f0baae0f465c53557..1592a2f56de443ddc298ad847a17f48175e04b02 100644 (file)
@@ -48,10 +48,10 @@ static PyObject *
 writeToSCL(PyObject *self, PyObject *args)
 {
     if (currentEnv != NULL && sclWriter != NULL) {
-       JNIEnv *env = currentEnv;
-
                Py_UNICODE *what;
                Py_ssize_t length;
+       JNIEnv *env = currentEnv;
+
                if (!PyArg_ParseTuple(args, "u#", &what, &length))
                        Py_RETURN_NONE;
 
@@ -75,30 +75,62 @@ writeToSCL(PyObject *self, PyObject *args)
        Py_RETURN_NONE;
 }
 
+static PyObject *
+flushSCL(PyObject *self, PyObject *args)
+{
+    if (currentEnv != NULL && sclWriter != NULL) {
+       JNIEnv *env = currentEnv;
+       PyThreadState *my_ts = PyThreadState_Get();
+       if (my_ts != main_ts) {
+               // TODO: Process calls from other threads
+                       Py_RETURN_NONE;
+       }
+
+       {
+                       jclass writerClass = (*env)->FindClass(env, 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, "<init>", "()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;
                }
index 15d681d43b7c5e023fccc34604c79ce997bd29d2..e71e107040475b26a1afba11aa2cf0aa6f07953c 100644 (file)
-#ifndef __MAIN_H__\r
-#define __MAIN_H__\r
-\r
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\r
-#ifdef _DEBUG\r
-       #undef _DEBUG\r
-       #include <Python.h>  //header for system python import; add include paths\r
-       #include <numpy/arrayobject.h>\r
-       #define _DEBUG 1\r
-#else\r
-       #include <Python.h>  //header for system python import; add include paths\r
-       #include <numpy/arrayobject.h>\r
-#endif\r
-\r
-#include <jni.h> //java connection header\r
-\r
-/*  To use this exported function of dll, include this header\r
- *  in your project.\r
- */\r
-\r
-#ifdef BUILD_DLL\r
-    #define DLL_EXPORT __declspec(dllexport)\r
-#else\r
-    #define DLL_EXPORT __declspec(dllimport)\r
-#endif\r
-\r
-\r
-#define JAVA_MAXINT (0x7fffffff)\r
-\r
-#define PYTHON_EXCEPTION "org/simantics/pythonlink/PythonException"\r
-#define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException"\r
-#define OBJECT_CLASS "java/lang/Object"\r
-#define STRING_CLASS "java/lang/String"\r
-#define MAP_CLASS "java/util/Map"\r
-#define SET_CLASS "java/util/Set"\r
-\r
-#define WRITER_CLASS "java/io/Writer"\r
-#define SCL_REPORTING_WRITER_CLASS "org/simantics/scl/runtime/reporting/SCLReportingWriter"\r
-\r
-#define PACKAGE_PREFIX "org/simantics/pythonlink/"\r
-\r
-#define NDARRAY_CLASS (PACKAGE_PREFIX "NDArray")\r
-\r
-#define VARIANT_CLASS "org/simantics/databoard/binding/mutable/Variant"\r
-#define BINDINGS_CLASS "org/simantics/databoard/Bindings"\r
-#define BINDING_CLASS "org/simantics/databoard/binding/Binding"\r
-#define DATATYPE_CLASS "org/simantics/databoard/type/Datatype"\r
-\r
-#define BOOLEANTYPE_CLASS "org/simantics/databoard/type/BooleanType"\r
-#define BYTETYPE_CLASS "org/simantics/databoard/type/ByteType"\r
-#define INTEGERTYPE_CLASS "org/simantics/databoard/type/IntegerType"\r
-#define LONGTYPE_CLASS "org/simantics/databoard/type/LongType"\r
-#define FLOATTYPE_CLASS "org/simantics/databoard/type/FloatType"\r
-#define DOUBLETYPE_CLASS "org/simantics/databoard/type/DoubleType"\r
-#define STRINGTYPE_CLASS "org/simantics/databoard/type/StringType"\r
-#define RECORDTYPE_CLASS "org/simantics/databoard/type/RecordType"\r
-#define ARRAYTYPE_CLASS "org/simantics/databoard/type/ArrayType"\r
-#define MAPTYPE_CLASS "org/simantics/databoard/type/MapType"\r
-#define OPTIONALTYPE_CLASS "org/simantics/databoard/type/OptionalType"\r
-#define UNIONTYPE_CLASS "org/simantics/databoard/type/UnionType"\r
-#define VARIANTTYPE_CLASS "org/simantics/databoard/type/VariantType"\r
-\r
-#define BOOLEANBINDING_CLASS "org/simantics/databoard/binding/BooleanBinding"\r
-#define BYTEBINDING_CLASS "org/simantics/databoard/binding/ByteBinding"\r
-#define INTEGERBINDING_CLASS "org/simantics/databoard/binding/IntegerBinding"\r
-#define LONGBINDING_CLASS "org/simantics/databoard/binding/LongBinding"\r
-#define FLOATBINDING_CLASS "org/simantics/databoard/binding/FloatBinding"\r
-#define DOUBLEBINDING_CLASS "org/simantics/databoard/binding/DoubleBinding"\r
-#define STRINGBINDING_CLASS "org/simantics/databoard/binding/StringBinding"\r
-#define RECORDBINDING_CLASS "org/simantics/databoard/binding/RecordBinding"\r
-#define ARRAYBINDING_CLASS "org/simantics/databoard/binding/ArrayBinding"\r
-#define MAPBINDING_CLASS "org/simantics/databoard/binding/MapBinding"\r
-#define OPTIONALBINDING_CLASS "org/simantics/databoard/binding/OptionalBinding"\r
-#define UNIONBINDING_CLASS "org/simantics/databoard/binding/UnionBinding"\r
-#define VARIANTBINDING_CLASS "org/simantics/databoard/binding/VariantBinding"\r
-\r
-#define COMPONENT_CLASS "org/simantics/databoard/type/Component"\r
-#define TAGGEDOBJECT_CLASS "org/simantics/databoard/binding/mutable/TaggedObject"\r
-\r
-PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value);\r
-PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value);\r
-PyObject *getPythonIntegerList(JNIEnv *env, jintArray value);\r
-PyObject *getPythonLongList(JNIEnv *env, jlongArray value);\r
-PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value);\r
-PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value);\r
-\r
-PyObject *getPythonObject(JNIEnv *env, jobject object, jobject binding);\r
-\r
-PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonRecordObject(JNIEnv *env, jobjectArray object, jobject binding);\r
-PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding);\r
-PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding);\r
-\r
-void setPythonVariable(PyObject *module, PyObject *name, PyObject *value);\r
-\r
-jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value);\r
-jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value);\r
-jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value);\r
-jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value);\r
-jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string);\r
-jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq);\r
-jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *list);\r
-jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *list);\r
-jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *list);\r
-jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *list);\r
-jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict);\r
-jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array);\r
-jobject pythonObjectAsObject(JNIEnv *env, PyObject *value);\r
-\r
-#endif // __MAIN_H__\r
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#ifdef _DEBUG
+       #undef _DEBUG
+       #include <Python.h>  //header for system python import; add include paths
+       #include <numpy/arrayobject.h>
+       #define _DEBUG 1
+#else
+       #include <Python.h>  //header for system python import; add include paths
+       #include <numpy/arrayobject.h>
+#endif
+
+#include <jni.h> //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__
index 16b02348196b4326f70f4475173a94677928c433..62703f0795f0d80c6b915cf5c4022d7cfa3c71cd 100644 (file)
@@ -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<Listener> 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 (file)
index 0000000..0bab712
--- /dev/null
@@ -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]);
+                       }
+               }
+       }
+}