From 15304b7c3168ef89c47dcfcfb4cf34c72000a75f Mon Sep 17 00:00:00 2001 From: tuorjr Date: Tue, 4 Oct 2016 16:22:17 +0000 Subject: [PATCH] Added support for dynamically typed data access and a Variable interface. Note: Proper function of the Variable interface depends on enhancement #6738. git-svn-id: https://www.simantics.org/svn/simantics-incubator/reino@1694 e36c2e66-7d30-0410-bdb2-d9e1f5a6d952 --- .../jnipython.dll | Bin 18944 -> 32768 bytes .../src/sclpy.c | 818 ++++++++++++++++-- .../src/sclpy.h | 101 +++ org.simantics.pythonlink/META-INF/MANIFEST.MF | 9 +- org.simantics.pythonlink/runAllTests.launch | 25 +- .../runScriptTests.launch | 44 +- org.simantics.pythonlink/scl/Python.scl | 44 +- .../scl/PythonVariable.scl | 5 + .../src/org/simantics/pythonlink/Python.java | 56 ++ .../simantics/pythonlink/PythonContext.java | 224 ++++- .../pythonlink/variable/PythonNode.java | 14 + .../variable/PythonNodeManager.java | 281 ++++++ .../simantics/pythonlink/test/AllTests.java | 2 +- .../pythonlink/test/ScriptTests.java | 25 + .../pythonlink/test/TestPythonVariable.java | 139 +++ .../pythonlink/test/scripts/Matplotlib.sts | 28 + .../pythonlink/test/scripts/NDArray.sts | 24 + .../pythonlink/test/scripts/Python.sts | 36 +- .../pythonlink/test/scripts/Variable.sts | 35 + .../pythonlink/test/scripts/Variant.sts | 22 + 20 files changed, 1799 insertions(+), 133 deletions(-) create mode 100644 org.simantics.pythonlink/scl/PythonVariable.scl create mode 100644 org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNode.java create mode 100644 org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java create mode 100644 org.simantics.pythonlink/test/org/simantics/pythonlink/test/TestPythonVariable.java create mode 100644 org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Matplotlib.sts create mode 100644 org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/NDArray.sts create mode 100644 org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variable.sts create mode 100644 org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variant.sts diff --git a/org.simantics.pythonlink.win32.x86_64/jnipython.dll b/org.simantics.pythonlink.win32.x86_64/jnipython.dll index 1960f88295359bb58dfe1cdf4aec385d0841001f..fb4cf4aa31ebd203dcabbfd07966e204eeebf160 100644 GIT binary patch literal 32768 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 diff --git a/org.simantics.pythonlink.win32.x86_64/src/sclpy.c b/org.simantics.pythonlink.win32.x86_64/src/sclpy.c index 4f5da9b..0db1e38 100644 --- a/org.simantics.pythonlink.win32.x86_64/src/sclpy.c +++ b/org.simantics.pythonlink.win32.x86_64/src/sclpy.c @@ -10,33 +10,10 @@ // // /////////////////////////////////////////////////////// -#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 "sclpy.h" -#include //java connection header - #include -#define JAVA_MAXINT (0x7fffffff) - -#define RUNTIME_EXCEPTION "java/lang/RuntimeException" -#define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException" -#define STRING_CLASS "java/lang/String" - -#define PACKAGE_PREFIX "org/simantics/pythonlink/" - -#define NDARRAY_CLASS (PACKAGE_PREFIX "NDArray") - jint throwException( JNIEnv *env, char *className, char *message ) { jclass exClass = (*env)->FindClass( env, className); @@ -52,9 +29,20 @@ jint throwIllegalArgumentException( JNIEnv *env, char *message ) { } int moduleCount = 0; +int initCalled = 0; +int hasNumpy = 0; JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) { char name[16]; + + if (!initCalled) { + Py_Initialize(); + initCalled = 1; + + hasNumpy = _import_array(); + hasNumpy = hasNumpy != -1; + } + sprintf(name, "SCL_%d", ++moduleCount); { @@ -62,6 +50,7 @@ JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContex PyObject *main = PyImport_AddModule("__main__"); PyDict_Merge(PyModule_GetDict(module), PyModule_GetDict(main), 0); + return (jlong)module; } } @@ -71,6 +60,15 @@ JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_deleteContext Py_XDECREF(module); } +PyObject *getPythonBool(jboolean value) { + if (value) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +} + PyObject *getPythonString(JNIEnv *env, jstring string) { jsize len = (*env)->GetStringLength(env, string); const jchar *chars = (*env)->GetStringChars(env, string, NULL); @@ -102,6 +100,30 @@ PyObject *getPythonStringList(JNIEnv *env, jobjectArray value) { return result; } +PyObject *getPythonBooleanList(JNIEnv *env, jbooleanArray value) { + jsize nitems = (*env)->GetArrayLength(env, value); + jboolean *values = (*env)->GetBooleanArrayElements(env, value, NULL); + jint i; + + PyObject *result = PyList_New(nitems); + for (i = 0; i < nitems; i++) { + PyList_SetItem(result, i, getPythonBool(values[i])); + } + + (*env)->ReleaseBooleanArrayElements(env, value, values, JNI_ABORT); + return result; +} + +PyObject *getPythonByteArray(JNIEnv *env, jbyteArray value) { + jint len = (*env)->GetArrayLength(env, value); + jbyte *values = (*env)->GetByteArrayElements(env, value, NULL); + + PyObject *result = PyByteArray_FromStringAndSize(values, len); + + (*env)->ReleaseByteArrayElements(env, value, values, JNI_ABORT); + return result; +} + PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) { jsize nitems = (*env)->GetArrayLength(env, value); jint *values = (*env)->GetIntArrayElements(env, value, NULL); @@ -116,6 +138,34 @@ PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) { return result; } +PyObject *getPythonLongList(JNIEnv *env, jlongArray value) { + jsize nitems = (*env)->GetArrayLength(env, value); + jlong *values = (*env)->GetLongArrayElements(env, value, NULL); + jint i; + + PyObject *result = PyList_New(nitems); + for (i = 0; i < nitems; i++) { + PyList_SetItem(result, i, PyLong_FromLongLong(values[i])); + } + + (*env)->ReleaseLongArrayElements(env, value, values, JNI_ABORT); + return result; +} + +PyObject *getPythonFloatList(JNIEnv *env, jfloatArray value) { + jsize nitems = (*env)->GetArrayLength(env, value); + float *values = (*env)->GetFloatArrayElements(env, value, NULL); + jint i; + + PyObject *result = PyList_New(nitems); + for (i = 0; i < nitems; i++) { + PyList_SetItem(result, i, PyFloat_FromDouble((double)values[i])); + } + + (*env)->ReleaseFloatArrayElements(env, value, values, JNI_ABORT); + return result; +} + PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value) { jsize nitems = (*env)->GetArrayLength(env, value); double *values = (*env)->GetDoubleArrayElements(env, value, NULL); @@ -168,6 +218,274 @@ PyObject *getPythonNDArray(JNIEnv *env, jobject value) { } } +PyObject *getPythonBooleanObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, BOOLEANBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)Z"); + + jboolean bvalue = (*env)->CallBooleanMethod(env, binding, getValueMethod, object); + return getPythonBool(bvalue); +} + +PyObject *getPythonByteObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, BYTEBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)B"); + + jbyte v = (*env)->CallByteMethod(env, binding, getValueMethod, object); + return PyLong_FromLong(v); +} + +PyObject *getPythonIntegerObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, INTEGERBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)I"); + + jint v = (*env)->CallIntMethod(env, binding, getValueMethod, object); + return PyLong_FromLong(v); +} + +PyObject *getPythonLongObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, LONGBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)J"); + + jlong v = (*env)->CallLongMethod(env, binding, getValueMethod, object); + return PyLong_FromLongLong(v); +} + +PyObject *getPythonFloatObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, FLOATBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)F"); + + jfloat v = (*env)->CallFloatMethod(env, binding, getValueMethod, object); + return PyFloat_FromDouble(v); +} + +PyObject *getPythonDoubleObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, DOUBLEBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue_", "(L" OBJECT_CLASS ";)D"); + + jdouble v = (*env)->CallDoubleMethod(env, binding, getValueMethod, object); + return PyFloat_FromDouble(v); +} + +PyObject *getPythonStringObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, STRINGBINDING_CLASS); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" STRING_CLASS ";"); + + jobject string = (*env)->CallObjectMethod(env, binding, getValueMethod, object); + jsize len = (*env)->GetStringLength(env, string); + const jchar *chars = (*env)->GetStringChars(env, string, NULL); + + PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL); + + (*env)->ReleaseStringChars(env, string, chars); + return value; +} + +PyObject *getPythonRecordObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, RECORDBINDING_CLASS); + jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";"); + jmethodID getComponent = (*env)->GetMethodID(env, bindingClass, "getComponent", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";"); + jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";"); + + jclass recordType = (*env)->FindClass(env, RECORDTYPE_CLASS); + jmethodID getTypeComponent = (*env)->GetMethodID(env, recordType, "getComponent", "(I)L" COMPONENT_CLASS ";"); + jmethodID getComponentCount = (*env)->GetMethodID(env, recordType, "getComponentCount", "()I"); + + jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS); + jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";"); + + jobject type = (*env)->CallObjectMethod(env, binding, typeMethod); + jint n = (*env)->CallIntMethod(env, type, getComponentCount); + jint i; + + PyObject *result = PyDict_New(); + for (i = 0; i < n; i++) { + jobject recordTypeComponent = (*env)->CallObjectMethod(env, type, getComponent, i); + jstring fieldName = (jstring)(*env)->GetObjectField(env, recordTypeComponent, nameField); + jobject componentObject = (*env)->CallObjectMethod(env, binding, getComponent, object, i); + jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, i); + + PyObject *item = getPythonObject(env, componentObject, componentBinding); + PyDict_SetItem(result, getPythonString(env, fieldName), item); + } + + return result; +} + +PyObject *getPythonArrayObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, ARRAYBINDING_CLASS); + jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";"); + jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I"); + jmethodID getMethod = (*env)->GetMethodID(env, bindingClass, "get", "(L" OBJECT_CLASS ";I)L" OBJECT_CLASS ";"); + + jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod); + + jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object); + + PyObject *result = PyList_New(size); + + jint i; + for (i = 0; i < size; i++) { + jobject item = (*env)->CallObjectMethod(env, binding, getMethod, object, i); + if (item != NULL) + PyList_SetItem(result, i, getPythonObject(env, item, componentBinding)); + else + PyList_SetItem(result, i, Py_None); + } + + return result; +} + +PyObject *getPythonMapObject(JNIEnv *env, jobject object, jobject binding) { + jclass objectClass = (*env)->FindClass(env, OBJECT_CLASS); + jclass bindingClass = (*env)->FindClass(env, MAPBINDING_CLASS); + jmethodID getKeyBindingMethod = (*env)->GetMethodID(env, bindingClass, "getKeyBinding", "()L" BINDING_CLASS ";"); + jmethodID getValueBindingMethod = (*env)->GetMethodID(env, bindingClass, "getValueBinding", "()L" BINDING_CLASS ";"); + jmethodID sizeMethod = (*env)->GetMethodID(env, bindingClass, "size", "(L" OBJECT_CLASS ";)I"); + jmethodID getAllMethod = (*env)->GetMethodID(env, bindingClass, "getAll", "(L" OBJECT_CLASS ";[L" OBJECT_CLASS ";[L" OBJECT_CLASS ";)V"); + + jobject keyBinding = (*env)->CallObjectMethod(env, binding, getKeyBindingMethod); + jobject valueBinding = (*env)->CallObjectMethod(env, binding, getValueBindingMethod); + + jint size = (*env)->CallIntMethod(env, binding, sizeMethod, object); + jobjectArray keys = (*env)->NewObjectArray(env, size, objectClass, NULL); + jobjectArray values = (*env)->NewObjectArray(env, size, objectClass, NULL); + + PyObject *result = PyDict_New(); + jint i; + + (*env)->CallVoidMethod(env, binding, getAllMethod, object, keys, values); + + for (i = 0; i < size; i++) { + jobject key = (*env)->GetObjectArrayElement(env, keys, i); + jobject item = (*env)->GetObjectArrayElement(env, values, i); + PyDict_SetItem(result, getPythonObject(env, key, keyBinding), getPythonObject(env, item, valueBinding)); + } + + (*env)->DeleteLocalRef(env, keys); + (*env)->DeleteLocalRef(env, values); + + return result; +} + +PyObject *getPythonOptionalObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, OPTIONALBINDING_CLASS); + jmethodID hasValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)Z"); + + jboolean hasValue = (*env)->CallBooleanMethod(env, binding, hasValueMethod, object); + + if (hasValue) { + jmethodID componentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "()L" BINDING_CLASS ";"); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "hasValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); + + jobject componentBinding = (*env)->CallObjectMethod(env, binding, componentBindingMethod); + jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object); + + return getPythonObject(env, value, componentBinding); + } + else { + return Py_None; + } +} + +PyObject *getPythonUnionObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, UNIONBINDING_CLASS); + jmethodID typeMethod = (*env)->GetMethodID(env, bindingClass, "type", "()L" RECORDTYPE_CLASS ";"); + jmethodID getTagMethod = (*env)->GetMethodID(env, bindingClass, "getTag", "(L" OBJECT_CLASS ";)I"); + jmethodID getValueMethod = (*env)->GetMethodID(env, bindingClass, "getValue", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); + jmethodID getComponentBinding = (*env)->GetMethodID(env, bindingClass, "getComponentBinding", "(I)L" BINDING_CLASS ";"); + + jclass unionType = (*env)->FindClass(env, UNIONTYPE_CLASS); + jmethodID getTypeComponent = (*env)->GetMethodID(env, unionType, "getComponent", "(I)L" COMPONENT_CLASS ";"); + + jclass componentClass = (*env)->FindClass(env, COMPONENT_CLASS); + jfieldID nameField = (*env)->GetFieldID(env, componentClass, "name", "L" STRING_CLASS ";"); + + jint tag = (*env)->CallIntMethod(env, binding, getTagMethod, object); + jobject value = (*env)->CallObjectMethod(env, binding, getValueMethod, object); + + jobject type = (*env)->CallObjectMethod(env, binding, typeMethod); + jobject typeComponent = (*env)->CallObjectMethod(env, type, getTypeComponent, tag); + jstring compName = (*env)->GetObjectField(env, typeComponent, nameField); + + jobject componentBinding = (*env)->CallObjectMethod(env, binding, getComponentBinding, tag); + + PyObject *result = PyTuple_New(2); + PyTuple_SetItem(result, 0, getPythonString(env, compName)); + PyTuple_SetItem(result, 1, getPythonObject(env, value, componentBinding)); + + return result; +} + +PyObject *getPythonVariantObject(JNIEnv *env, jobject object, jobject binding) { + jclass bindingClass = (*env)->FindClass(env, VARIANTBINDING_CLASS); + jmethodID getContentMethod = (*env)->GetMethodID(env, bindingClass, "getContent", "(L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); + jmethodID getContentBindingMethod = (*env)->GetMethodID(env, bindingClass, "getContentBinding", "(L" OBJECT_CLASS ";)L" BINDING_CLASS ";"); + + jobject content = (*env)->CallObjectMethod(env, binding, getContentMethod, object); + jobject contentBinding = (*env)->CallObjectMethod(env, binding, getContentBindingMethod, object); + + return getPythonObject(env, content, contentBinding); +} + +PyObject *getPythonObject(JNIEnv *env, jobject value, jobject binding) { + jclass booleanBinding = (*env)->FindClass(env, BOOLEANBINDING_CLASS); + jclass byteBinding = (*env)->FindClass(env, BYTEBINDING_CLASS); + jclass integerBinding = (*env)->FindClass(env, INTEGERBINDING_CLASS); + jclass longBinding = (*env)->FindClass(env, LONGBINDING_CLASS); + jclass floatBinding = (*env)->FindClass(env, FLOATBINDING_CLASS); + jclass doubleBinding = (*env)->FindClass(env, DOUBLEBINDING_CLASS); + jclass stringBinding = (*env)->FindClass(env, STRINGBINDING_CLASS); + jclass recordBinding = (*env)->FindClass(env, RECORDBINDING_CLASS); + jclass arrayBinding = (*env)->FindClass(env, ARRAYBINDING_CLASS); + jclass mapBinding = (*env)->FindClass(env, MAPBINDING_CLASS); + jclass optionalBinding = (*env)->FindClass(env, OPTIONALBINDING_CLASS); + jclass untionBinding = (*env)->FindClass(env, UNIONBINDING_CLASS); + jclass variantBinding = (*env)->FindClass(env, VARIANTBINDING_CLASS); + + if ((*env)->IsInstanceOf(env, binding, booleanBinding)) { + return getPythonBooleanObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, byteBinding)) { + return getPythonByteObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, integerBinding)) { + return getPythonIntegerObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, longBinding)) { + return getPythonLongObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, floatBinding)) { + return getPythonFloatObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, doubleBinding)) { + return getPythonDoubleObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, stringBinding)) { + return getPythonStringObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, recordBinding)) { + return getPythonRecordObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, arrayBinding)) { + return getPythonArrayObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, mapBinding)) { + return getPythonMapObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, optionalBinding)) { + return getPythonOptionalObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, untionBinding)) { + return getPythonUnionObject(env, value, binding); + } + else if ((*env)->IsInstanceOf(env, binding, variantBinding)) { + return getPythonVariantObject(env, value, binding); + } + else { + return Py_None; + } +} + void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) { if (name && value) { PyDict_SetItem(PyModule_GetDict(module), name, value); @@ -177,6 +495,62 @@ void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) { Py_XDECREF(value); } +static npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { + if (d == nd) { + ncont[d] = 1; + return 1; + } + else { + npy_intp n = nContiguous(d+1, nd, strides, dims, ncont); + ncont[d] = n > 0 && strides[d] == sizeof(double) * n ? dims[d] * n : 0; + return ncont[d]; + } +} + +static void copyDoubleArrayValues(JNIEnv *env, jdoubleArray array, double *data, npy_intp *offset, int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { + if (ncont[d] > 0) { + (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data); + *offset += ncont[d]; + } + else { + int i; + for (i = 0; i < dims[d]; i++) { + copyDoubleArrayValues(env, array, (double*)((char*)data + strides[d] * i), offset, d+1, nd, strides, dims, ncont); + } + } +} + +jobject pythonBoolAsBooleanObject(JNIEnv *env, PyObject *value) { + jclass booleanClass = (*env)->FindClass(env, "java/lang/Boolean"); + jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;"); + + return (*env)->CallStaticObjectMethod(env, booleanClass, valueOfMethod, (jboolean)(value == Py_True)); +} + +jobject pythonLongAsLongObject(JNIEnv *env, PyObject *value) { + jclass longClass = (*env)->FindClass(env, "java/lang/Long"); + jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, longClass, "valueOf", "(J)Ljava/lang/Long;"); + + return (*env)->CallStaticObjectMethod(env, longClass, valueOfMethod, PyLong_AsLongLong(value)); +} + +jobject pythonFloatAsDoubleObject(JNIEnv *env, PyObject *value) { + jclass doubleClass = (*env)->FindClass(env, "java/lang/Double"); + jmethodID valueOfMethod = (*env)->GetStaticMethodID(env, doubleClass, "valueOf", "(D)Ljava/lang/Double;"); + + return (*env)->CallStaticObjectMethod(env, doubleClass, valueOfMethod, PyFloat_AsDouble(value)); +} + +jobject pythonByteArrayAsByteArray(JNIEnv *env, PyObject *value) { + Py_ssize_t size = PyByteArray_Size(value); + jbyteArray result = (*env)->NewByteArray(env, (jsize)size); + char *bytes = PyByteArray_AsString(value); + + (*env)->SetByteArrayRegion(env, result, 0, size, bytes); + + return result; +} + jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string) { PyObject *utf16Value = PyUnicode_AsUTF16String(string); Py_ssize_t len = PyBytes_Size(utf16Value) / 2; @@ -190,15 +564,15 @@ jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string) { return result; } -jobjectArray pythonStringListAsJavaArray(JNIEnv *env, PyObject *list) { - Py_ssize_t len = PyList_Size(list); +jobjectArray pythonSequenceAsStringArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, STRING_CLASS), NULL); jint i; for (i = 0; i < jlen; i++) { - PyObject *item = PyList_GetItem(list, i); + PyObject *item = PySequence_GetItem(seq, i); if (PyUnicode_Check(item)) { jstring value = pythonStringAsJavaString(env, item); (*env)->SetObjectArrayElement(env, array, i, value); @@ -212,15 +586,15 @@ jobjectArray pythonStringListAsJavaArray(JNIEnv *env, PyObject *list) { return array; } -jdoubleArray pythonListAsDoubleArray(JNIEnv *env, PyObject *list) { - Py_ssize_t len = PyList_Size(list); +jdoubleArray pythonSequenceAsDoubleArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); jsize jlen = (jsize)min(len, JAVA_MAXINT); jdoubleArray array = (*env)->NewDoubleArray(env, jlen); jint i; for (i = 0; i < jlen; i++) { - PyObject *item = PyList_GetItem(list, i); + PyObject *item = PySequence_GetItem(seq, i); if (PyFloat_Check(item)) { double value = PyFloat_AsDouble(item); (*env)->SetDoubleArrayRegion(env, array, i, 1, &value); @@ -234,29 +608,107 @@ jdoubleArray pythonListAsDoubleArray(JNIEnv *env, PyObject *list) { return array; } -npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { - if (d == nd) { - ncont[d] = 1; - return 1; +jobject pythonObjectAsObject(JNIEnv *env, PyObject *value) { + if (PyBool_Check(value)) + return pythonBoolAsBooleanObject(env, value); + else if (PyLong_Check(value)) + return pythonLongAsLongObject(env, value); + else if (PyFloat_Check(value)) + return pythonFloatAsDoubleObject(env, value); + else if (PyUnicode_Check(value)) + return pythonStringAsJavaString(env, value); + else if (PyByteArray_Check(value)) + return pythonByteArrayAsByteArray(env, value); + else if (PyDict_Check(value)) + return pythonDictionaryAsMap(env, value); + else if (hasNumpy && PyArray_Check(value)) + return pythonArrayAsNDArray(env, (PyArrayObject *)value); + else if (PySequence_Check(value)) + return pythonSequenceAsObjectArray(env, value); + else + return NULL; +} + +jobjectArray pythonSequenceAsObjectArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); + jsize jlen = (jsize)min(len, JAVA_MAXINT); + jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, OBJECT_CLASS), NULL); + + jint i; + + for (i = 0; i < jlen; i++) { + PyObject *item = PySequence_GetItem(seq, i); + jobject object = pythonObjectAsObject(env, item); + (*env)->SetObjectArrayElement(env, array, i, object); } - else { - npy_intp n = nContiguous(d+1, nd, strides, dims, ncont); - ncont[d] = n > 0 && strides[d] == sizeof(double) * n ? dims[d] * n : 0; - return ncont[d]; + + return array; +} + +jbooleanArray pythonSequenceAsBooleanArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); + jsize jlen = (jsize)min(len, JAVA_MAXINT); + jbooleanArray array = (*env)->NewBooleanArray(env, jlen); + + jint i; + + for (i = 0; i < jlen; i++) { + PyObject *item = PySequence_GetItem(seq, i); + if (PyBool_Check(item)) { + jboolean value = item == Py_True; + (*env)->SetBooleanArrayRegion(env, array, i, 1, &value); + } + else { + throwException(env, RUNTIME_EXCEPTION, "List item not a boolean"); + return NULL; + } } + + return array; } -void copyDoubleArrayValues(JNIEnv *env, jdoubleArray array, double *data, npy_intp *offset, int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) { - if (ncont[d] > 0) { - (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data); - *offset += ncont[d]; +jintArray pythonSequenceAsIntegerArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); + jsize jlen = (jsize)min(len, JAVA_MAXINT); + jintArray array = (*env)->NewIntArray(env, jlen); + + jint i; + + for (i = 0; i < jlen; i++) { + PyObject *item = PySequence_GetItem(seq, i); + if (PyLong_Check(item)) { + jint value = PyLong_AsLong(item); + (*env)->SetIntArrayRegion(env, array, i, 1, &value); + } + else { + throwException(env, RUNTIME_EXCEPTION, "List item not an integer"); + return NULL; + } } - else { - int i; - for (i = 0; i < dims[d]; i++) { - copyDoubleArrayValues(env, array, (double*)((char*)data + strides[d] * i), offset, d+1, nd, strides, dims, ncont); + + return array; +} + +jlongArray pythonSequenceAsLongArray(JNIEnv *env, PyObject *seq) { + Py_ssize_t len = PySequence_Size(seq); + jsize jlen = (jsize)min(len, JAVA_MAXINT); + jlongArray array = (*env)->NewLongArray(env, jlen); + + jint i; + + for (i = 0; i < jlen; i++) { + PyObject *item = PySequence_GetItem(seq, i); + if (PyLong_Check(item)) { + jlong value = PyLong_AsLongLong(item); + (*env)->SetLongArrayRegion(env, array, i, 1, &value); + } + else { + throwException(env, RUNTIME_EXCEPTION, "List item not an integer"); + return NULL; } } + + return array; } jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array) { @@ -303,33 +755,49 @@ jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array) { } } -jintArray pythonListAsIntegerArray(JNIEnv *env, PyObject *list) { - Py_ssize_t len = PyList_Size(list); - jsize jlen = (jsize)min(len, JAVA_MAXINT); - jdoubleArray array = (*env)->NewIntArray(env, jlen); +jobject pythonDictionaryAsMap(JNIEnv *env, PyObject *dict) { + jclass hashmapClass = (*env)->FindClass(env, "java/util/HashMap"); + jmethodID constructor = (*env)->GetMethodID(env, hashmapClass, "", "(I)V"); + jmethodID putMethod = (*env)->GetMethodID(env, hashmapClass, "put", "(L" OBJECT_CLASS ";L" OBJECT_CLASS ";)L" OBJECT_CLASS ";"); - jint i; + Py_ssize_t size = PyDict_Size(dict); + jobject map = (*env)->NewObject(env, hashmapClass, constructor, (jint)size); - for (i = 0; i < jlen; i++) { - PyObject *item = PyList_GetItem(list, i); - if (PyLong_Check(item)) { - jint value = PyLong_AsLong(item); - (*env)->SetIntArrayRegion(env, array, i, 1, &value); - } - else { - throwException(env, RUNTIME_EXCEPTION, "List item not an integer"); - return NULL; - } + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(dict, &pos, &key, &value)) { + jobject keyObject = pythonObjectAsObject(env, key); + jobject valueObject = pythonObjectAsObject(env, value); + (*env)->CallObjectMethod(env, map, putMethod, keyObject, valueObject); } - return array; + return map; } -JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonIntegerVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jint value) { +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonBooleanVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jboolean value) { PyObject *module = (PyObject*)contextID; PyObject *pythonName = getPythonString(env, variableName); - PyObject *val = PyLong_FromLong(value); + PyObject *val = getPythonBool(value); + + setPythonVariable(module, pythonName, val); +} + +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonBooleanArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jbooleanArray value) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + PyObject *val = getPythonBooleanList(env, value); + + setPythonVariable(module, pythonName, val); +} + +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonLongVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jlong value) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + PyObject *val = PyLong_FromLongLong(value); setPythonVariable(module, pythonName, val); } @@ -343,6 +811,15 @@ JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonInte setPythonVariable(module, pythonName, val); } +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonLongArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jlongArray value) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + PyObject *val = getPythonLongList(env, value); + + setPythonVariable(module, pythonName, val); +} + JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdouble value) { PyObject *module = (PyObject*)contextID; @@ -352,6 +829,15 @@ JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoub setPythonVariable(module, pythonName, val); } +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonFloatArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jfloatArray value) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + PyObject *val = getPythonFloatList(env, value); + + setPythonVariable(module, pythonName, val); +} + JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdoubleArray value) { PyObject *module = (PyObject*)contextID; @@ -382,7 +868,7 @@ JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonStri JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value) { PyObject *module = (PyObject*)contextID; - if (_import_array() < 0) { + if (!hasNumpy) { throwException(env, RUNTIME_EXCEPTION, "Importing numpy failed"); return; } @@ -395,6 +881,15 @@ JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonNDAr } } +JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonVariantVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value, jobject binding) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + PyObject *val = getPythonObject(env, value, binding); + + setPythonVariable(module, pythonName, val); +} + JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) { PyObject *module = (PyObject*)contextID; @@ -418,8 +913,6 @@ JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePython throwException(env, RUNTIME_EXCEPTION, message); } - // Py_XDECREF(globals); - (*env)->ReleaseStringUTFChars(env, statement, utfchars); return result != NULL ? 0 : 1; @@ -443,7 +936,10 @@ JNIEXPORT jstring JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonS return 0; } - return pythonStringAsJavaString(env, value); + { + jstring result = pythonStringAsJavaString(env, value); + return result; + } } JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonStringArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { @@ -457,15 +953,59 @@ JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPy return 0; } - if (!PyList_Check(value)) { - throwException(env, RUNTIME_EXCEPTION, "Python variable not a list"); + if (!PySequence_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a sequence"); + return 0; + } + + { + jobjectArray result = pythonSequenceAsStringArray(env, value); + return result; + } +} + +JNIEXPORT jboolean JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonBooleanVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + + PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); + if (value == NULL) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not found"); + return 0; + } + + if (!PyBool_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a boolean"); return 0; } - return pythonStringListAsJavaArray(env, value); + return value == Py_True; } -JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonIntegerVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { +JNIEXPORT jbooleanArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonBooleanArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + + PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); + if (value == NULL) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not found"); + return 0; + } + + if (!PySequence_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a sequence"); + return 0; + } + + { + jbooleanArray result = pythonSequenceAsBooleanArray(env, value); + return result; + } +} + +JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonLongVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { PyObject *module = (PyObject*)contextID; PyObject *pythonName = getPythonString(env, variableName); @@ -481,7 +1021,10 @@ JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonInte return 0; } - return PyLong_AsLong(value); + { + jlong result = PyLong_AsLongLong(value); + return result; + } } JNIEXPORT jintArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonIntegerArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { @@ -495,12 +1038,37 @@ JNIEXPORT jintArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPytho return NULL; } - if (!PyList_Check(value)) { - throwException(env, RUNTIME_EXCEPTION, "Python variable not a list"); + if (!PySequence_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a sequence"); return NULL; } - return pythonListAsIntegerArray(env, value); + { + jintArray result = pythonSequenceAsIntegerArray(env, value); + return result; + } +} + +JNIEXPORT jlongArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonLongArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + + PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); + if (value == NULL) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not found"); + return NULL; + } + + if (!PySequence_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a sequence"); + return NULL; + } + + { + jlongArray result = pythonSequenceAsLongArray(env, value); + return result; + } } JNIEXPORT jdouble JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { @@ -519,7 +1087,10 @@ JNIEXPORT jdouble JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonD return 0.0; } - return PyFloat_AsDouble(value); + { + jdouble result = PyFloat_AsDouble(value); + return result; + } } JNIEXPORT jdoubleArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { @@ -533,18 +1104,21 @@ JNIEXPORT jdoubleArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPy return NULL; } - if (!PyList_Check(value)) { - throwException(env, RUNTIME_EXCEPTION, "Python variable not a list"); + if (!PySequence_Check(value)) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not a sequence"); return NULL; } - return pythonListAsDoubleArray(env, value); + { + jdoubleArray result = pythonSequenceAsDoubleArray(env, value); + return result; + } } JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { PyObject *module = (PyObject*)contextID; - if (_import_array() < 0) { + if (!hasNumpy) { throwException(env, RUNTIME_EXCEPTION, "Importing numpy failed"); return NULL; } @@ -568,8 +1142,88 @@ JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonN return NULL; } - return pythonArrayAsNDArray(env, (PyArrayObject *)value); + { + jobject result = pythonArrayAsNDArray(env, (PyArrayObject *)value); + return result; + } + } +} + +JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariantVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { + PyObject *module = (PyObject*)contextID; + + PyObject *pythonName = getPythonString(env, variableName); + + PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName); + if (value == NULL) { + throwException(env, RUNTIME_EXCEPTION, "Python variable not found"); + return NULL; } + + hasNumpy = _import_array() != -1; + + { + jobject result = pythonObjectAsObject(env, value); + return result; + } +} + +JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableTypeImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) { + PyObject *module = (PyObject*)contextID; + PyObject *dict = PyModule_GetDict(module); + + PyObject *pythonName = getPythonString(env, variableName); + + if (!PyDict_Contains(dict, pythonName)) { + return 0; + } + + { + PyObject *value = PyDict_GetItem(dict, pythonName); + + jint result; + + if (PyBool_Check(value)) + result = 1; + else if (PyLong_Check(value)) + result = 2; + else if (PyFloat_Check(value)) + result = 3; + else if (PyUnicode_Check(value)) + result = 4; + else if (PyByteArray_Check(value)) + result = 5; + else if (PyDict_Check(value)) + result = 6; + else if (hasNumpy && PyArray_Check(value)) + result = 7; + else if (PySequence_Check(value)) + result = 8; + else + result = -1; + + return result; + } +} + +JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonVariableNamesImpl(JNIEnv *env, jobject thisObj, jlong contextID) { + PyObject *module = (PyObject*)contextID; + PyObject *dict = PyModule_GetDict(module); + + PyObject *keys = PyDict_Keys(dict); + Py_ssize_t size = PyList_Size(keys); + + jobjectArray result = (*env)->NewObjectArray(env, (jsize)size, (*env)->FindClass(env, STRING_CLASS), NULL); + + Py_ssize_t i; + for (i = 0; i < size; i++) { + jstring javaName = pythonStringAsJavaString(env, PyList_GetItem(keys, i)); + (*env)->SetObjectArrayElement(env, result, (jint)i, javaName); + } + + Py_XDECREF(keys); + + return result; } BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) @@ -580,12 +1234,10 @@ BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load - Py_Initialize(); break; case DLL_PROCESS_DETACH: // detach from process - Py_Finalize(); break; case DLL_THREAD_ATTACH: diff --git a/org.simantics.pythonlink.win32.x86_64/src/sclpy.h b/org.simantics.pythonlink.win32.x86_64/src/sclpy.h index ec3b400..4a2c7de 100644 --- a/org.simantics.pythonlink.win32.x86_64/src/sclpy.h +++ b/org.simantics.pythonlink.win32.x86_64/src/sclpy.h @@ -1,6 +1,19 @@ #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. */ @@ -11,4 +24,92 @@ #define DLL_EXPORT __declspec(dllimport) #endif + +#define JAVA_MAXINT (0x7fffffff) + +#define RUNTIME_EXCEPTION "java/lang/RuntimeException" +#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 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/META-INF/MANIFEST.MF b/org.simantics.pythonlink/META-INF/MANIFEST.MF index 14c621b..427f880 100644 --- a/org.simantics.pythonlink/META-INF/MANIFEST.MF +++ b/org.simantics.pythonlink/META-INF/MANIFEST.MF @@ -9,7 +9,14 @@ Require-Bundle: org.junit, org.simantics.scl.runtime, gnu.trove3;bundle-version="3.0.3", org.simantics.scl.compiler, - org.simantics.scl.osgi + org.simantics.scl.osgi, + org.simantics.db.layer0;bundle-version="1.1.0", + org.simantics.simulator.variable, + org.simantics, + org.simantics.application, + org.simantics.db.management, + org.simantics.scl.db;bundle-version="0.1.3", + org.simantics.modeling;bundle-version="1.1.1" Export-Package: org.simantics.pythonlink Bundle-Activator: org.simantics.pythonlink.Activator Bundle-ActivationPolicy: lazy diff --git a/org.simantics.pythonlink/runAllTests.launch b/org.simantics.pythonlink/runAllTests.launch index 8b34091..4444941 100644 --- a/org.simantics.pythonlink/runAllTests.launch +++ b/org.simantics.pythonlink/runAllTests.launch @@ -13,6 +13,7 @@ + @@ -33,12 +34,34 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.simantics.pythonlink/runScriptTests.launch b/org.simantics.pythonlink/runScriptTests.launch index b4c34eb..dadf334 100644 --- a/org.simantics.pythonlink/runScriptTests.launch +++ b/org.simantics.pythonlink/runScriptTests.launch @@ -35,12 +35,48 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/org.simantics.pythonlink/scl/Python.scl b/org.simantics.pythonlink/scl/Python.scl index b1ca052..5fc7831 100644 --- a/org.simantics.pythonlink/scl/Python.scl +++ b/org.simantics.pythonlink/scl/Python.scl @@ -1,5 +1,3 @@ -import "Vector" - effect Python "Simantics/Python/Python" "org.simantics.pythonlink.PythonContext" @@ -36,6 +34,27 @@ importJava "org.simantics.pythonlink.NDArray" where @JavaName equals ndarrayEquals :: NDArray -> NDArray -> Boolean +class NDArrayInput a where + ndarrayInput :: a -> ([Integer], [Double]) + +instance NDArrayInput [Double] where + ndarrayInput v = ([length v], v) + +instance (NDArrayInput [a]) => NDArrayInput [[a]] where + ndarrayInput v = let + inp = map ndarrayInput v + dims = map fst inp + vals = join $ map snd inp + in + if not (all (== (dims!0)) dims) then + fail "List of lists not of uniform dimension" + else + ([length v] + (dims!0), vals) + +ndarrayFromList :: NDArrayInput a => a -> NDArray +ndarrayFromList v = ndarrayN (vector dims) (vector vals) where + (dims, vals) = ndarrayInput v + instance Show NDArray where show = ndarrayToString @@ -50,22 +69,35 @@ importJava "org.simantics.pythonlink.PythonContext" where executePythonStatement :: String -> () + setPythonBooleanVariable :: String -> Boolean -> () setPythonIntegerVariable :: String -> Integer -> () - setPythonIntegerArrayVariable :: String -> Vector Integer -> () + setPythonLongVariable :: String -> Long -> () setPythonDoubleVariable :: String -> Double -> () - setPythonDoubleArrayVariable :: String -> Vector Double -> () setPythonStringVariable :: String -> String -> () + + setPythonBooleanArrayVariable :: String -> Vector Boolean -> () + setPythonIntegerArrayVariable :: String -> Vector Integer -> () + setPythonLongArrayVariable :: String -> Vector Long -> () + setPythonDoubleArrayVariable :: String -> Vector Double -> () setPythonStringArrayVariable :: String -> Vector String -> () setPythonNDArrayVariable :: String -> NDArray -> () + getPythonBooleanVariable :: String -> Boolean getPythonIntegerVariable :: String -> Integer - getPythonIntegerArrayVariable :: String -> Vector Integer + getPythonLongVariable :: String -> Long getPythonDoubleVariable :: String -> Double - getPythonDoubleArrayVariable :: String -> Vector Double getPythonStringVariable :: String -> String + + getPythonBooleanArrayVariable :: String -> Vector Boolean + getPythonIntegerArrayVariable :: String -> Vector Integer + getPythonLongArrayVariable :: String -> Vector Long + getPythonDoubleArrayVariable :: String -> Vector Double getPythonStringArrayVariable :: String -> Vector String getPythonNDArrayVariable :: String -> NDArray + setPythonVariantVariable :: String -> Variant -> () + getPythonVariantVariable :: String -> Variant + importJava "org.simantics.pythonlink.Python" where openPythonContext :: PythonContext diff --git a/org.simantics.pythonlink/scl/PythonVariable.scl b/org.simantics.pythonlink/scl/PythonVariable.scl new file mode 100644 index 0000000..55db31c --- /dev/null +++ b/org.simantics.pythonlink/scl/PythonVariable.scl @@ -0,0 +1,5 @@ +include "Python" +include "Simantics/Variables" + +importJava "org.simantics.pythonlink.Python" where + getPythonContextVariable :: PythonContext -> Variable diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/Python.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/Python.java index 07921fd..9d00c44 100644 --- a/org.simantics.pythonlink/src/org/simantics/pythonlink/Python.java +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/Python.java @@ -1,8 +1,20 @@ package org.simantics.pythonlink; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.simantics.db.layer0.variable.NodeSupport; +import org.simantics.db.layer0.variable.StandardGraphChildVariable; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.layer0.variable.VariableNode; +import org.simantics.pythonlink.PythonContext.Listener; +import org.simantics.pythonlink.variable.PythonNode; +import org.simantics.pythonlink.variable.PythonNodeManager; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function; import org.simantics.scl.runtime.tuple.Tuple0; +import org.simantics.simulator.variable.NodeManager; +import org.simantics.simulator.variable.exceptions.NodeManagerException; public class Python { private static final String PYTHON_CONTEXT = "Simantics/Python/Python"; @@ -11,6 +23,50 @@ public class Python { return new PythonContext(); } + public static Variable getPythonContextVariable(PythonContext context) { + NodeManager nodeManager = new PythonNodeManager(context); + PythonNode root; + try { + root = nodeManager.getNode("/"); + } catch (NodeManagerException e) { + // Should not happen + throw new RuntimeException("Getting root Python node failed"); + } + + final NodeSupport support = new NodeSupport(nodeManager, 0, TimeUnit.NANOSECONDS); + + context.addListener(new Listener() { + @Override + public void updated(String variableName) { + try { + PythonNode root = nodeManager.getNode("/"); + if (variableName != null) { + PythonNode node = nodeManager.getNode(variableName); + if (node != null) support.valueCache.removeListening(node); + support.structureCache.removeListening(root); + } + else { + List props = nodeManager.getProperties(root); + for (PythonNode p : props) + support.valueCache.removeListening(p); + support.structureCache.removeListening(root); + } + + support.valueCache.clearExpired(); + support.structureCache.clearExpired(); + } catch (NodeManagerException e) { + e.printStackTrace(); + } + } + + @Override + public void closed() { + } + }); + + return new StandardGraphChildVariable(null, new VariableNode(support, root), null); + } + @SuppressWarnings( { "unchecked", "rawtypes" } ) public static Object runPythonF(Function f) { SCLContext sclContext = SCLContext.getCurrent(); diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java index 5341cc2..6c96dd3 100644 --- a/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/PythonContext.java @@ -1,21 +1,73 @@ package org.simantics.pythonlink; import java.io.Closeable; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Pattern; + +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; public class PythonContext implements Closeable { private long contextID; + public interface Listener { + void updated(String variableName); + void closed(); + } + + static ExecutorService pythonExecutor = Executors.newSingleThreadExecutor(); + + Set listeners = new HashSet<>(); + + public enum VariableType { + NO_VARIABLE, + BOOLEAN, + LONG, + FLOAT, + STRING, + BYTEARRAY, + DICTIONARY, + NDARRAY, + SEQUENCE, + UNKNOWN + } + + static Pattern namePattern = Pattern.compile("([a-zA-Z_][a-zA-Z_0-9]*)"); + PythonContext() { contextID = createContextImpl(); } + public void addListener(Listener listener) { + listeners.add(listener); + } + + public void removeListener(Listener listener) { + listeners.remove(listener); + } + @Override public void close() { long id = contextID; contextID = 0; if (id != 0) deleteContextImpl(id); + + for (Listener l : listeners) { + l.closed(); + } } + public boolean isOpen() { + return contextID != 0; + } + @Override protected void finalize() throws Throwable { super.finalize(); @@ -23,82 +75,220 @@ public class PythonContext implements Closeable { } public void executePythonStatement(String statement) { - executePythonStatementImpl( contextID, statement ); + execute(() -> executePythonStatementImpl( contextID, statement )); + for (Listener l : listeners) { l.updated(null); } } // Setters + public void setPythonBooleanVariable(String variableName, boolean value) { + checkValidName(variableName); + execute(() -> setPythonBooleanVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonIntegerVariable(String variableName, int value) { - setPythonIntegerVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonLongVariable(String variableName, long value) { + checkValidName(variableName); + execute(() -> setPythonLongVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } public void setPythonDoubleVariable(String variableName, double value) { - setPythonDoubleVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonDoubleVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } public void setPythonStringVariable(String variableName, String value) { - setPythonStringVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonStringVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } + public void setPythonBooleanArrayVariable(String variableName, boolean[] value) { + checkValidName(variableName); + execute(() -> setPythonBooleanArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } public void setPythonIntegerArrayVariable(String variableName, int[] value) { - setPythonIntegerArrayVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonIntegerArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } + } + public void setPythonLongArrayVariable(String variableName, long[] value) { + checkValidName(variableName); + execute(() -> setPythonLongArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } public void setPythonDoubleArrayVariable(String variableName, double[] value) { - setPythonDoubleArrayVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonDoubleArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } public void setPythonStringArrayVariable(String variableName, String[] value) { - setPythonStringArrayVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonStringArrayVariableImpl(contextID, variableName, value)); + for (Listener l : listeners) { l.updated(variableName); } } // Getters + public boolean getPythonBooleanVariable(String variableName) { + checkValidName(variableName); + return getPythonBooleanVariableImpl(contextID, variableName); + } public int getPythonIntegerVariable(String variableName) { - return getPythonIntegerVariableImpl(contextID, variableName); + checkValidName(variableName); + long value = execute(() -> getPythonLongVariableImpl(contextID, variableName)); + if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) + throw new RuntimeException("Python value not in integer range"); + return (int) value; + } + public long getPythonLongVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonLongVariableImpl(contextID, variableName)); } public double getPythonDoubleVariable(String variableName) { - return getPythonDoubleVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonDoubleVariableImpl(contextID, variableName)); } public String getPythonStringVariable(String variableName) { - return getPythonStringVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonStringVariableImpl(contextID, variableName)); } + public boolean[] getPythonBooleanArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonBooleanArrayVariableImpl(contextID, variableName)); + } public int[] getPythonIntegerArrayVariable(String variableName) { - return getPythonIntegerArrayVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonIntegerArrayVariableImpl(contextID, variableName)); + } + public long[] getPythonLongArrayVariable(String variableName) { + checkValidName(variableName); + return execute(() -> getPythonLongArrayVariableImpl(contextID, variableName)); } public double[] getPythonDoubleArrayVariable(String variableName) { - return getPythonDoubleArrayVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonDoubleArrayVariableImpl(contextID, variableName)); } public String[] getPythonStringArrayVariable(String variableName) { - return getPythonStringArrayVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonStringArrayVariableImpl(contextID, variableName)); } public void setPythonNDArrayVariable(String variableName, NDArray value) { - setPythonNDArrayVariableImpl(contextID, variableName, value); + checkValidName(variableName); + execute(() -> setPythonNDArrayVariableImpl(contextID, variableName, value)); } public NDArray getPythonNDArrayVariable(String variableName) { - return getPythonNDArrayVariableImpl(contextID, variableName); + checkValidName(variableName); + return execute(() -> getPythonNDArrayVariableImpl(contextID, variableName)); } + public Object getPythonVariantVariable(String variableName, Binding binding) { + checkValidName(variableName); + Object result = execute(() -> getPythonVariantVariableImpl(contextID, variableName)); + try { + return Bindings.OBJECT.getContent(result, binding); + } catch (BindingException e) { + throw new RuntimeException(e); + } + } + + public Variant getPythonVariantVariable(String variableName) { + checkValidName(variableName); + return Variant.ofInstance(execute(() -> getPythonVariantVariableImpl(contextID, variableName))); + } + + public void setPythonVariantVariable(String variableName, Variant value) { + setPythonVariantVariable(variableName, value.getValue(), value.getBinding()); + } + + public void setPythonVariantVariable(String variableName, Object value, Binding binding) { + checkValidName(variableName); + if (!binding.isInstance(value)) throw new IllegalArgumentException("Invalid object binding"); + + execute(() -> setPythonVariantVariableImpl(contextID, variableName, value, binding)); + + for (Listener l : listeners) { l.updated(variableName); } + } + + public VariableType getPythonVariableType(String variableName) { + checkValidName(variableName); + int code = execute(() -> getPythonVariableTypeImpl(contextID, variableName)); + + VariableType[] values = VariableType.values(); + if (code < 0 || code >= values.length) + return VariableType.UNKNOWN; + + return values[code]; + } + + public String[] getPythonVariableNames() { + return execute(() -> getPythonVariableNamesImpl(contextID)); + } + + private static void checkValidName(String variableName) { + if (!namePattern.matcher(variableName).matches()) + throw new IllegalArgumentException("Invalid Python variable name " + variableName); + } + + static void execute(Runnable job) { + try { + pythonExecutor.submit(job).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + static V execute(Callable job) { + try { + return pythonExecutor.submit(job).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + // Native function declarations private static native long createContextImpl(); private static native void deleteContextImpl(long contextID); private static native int executePythonStatementImpl(long contextID, String statement); - private static native void setPythonIntegerVariableImpl(long contextID, String variableName, int value); + private static native void setPythonBooleanVariableImpl(long contextID, String variableName, boolean value); + private static native void setPythonLongVariableImpl(long contextID, String variableName, long value); private static native void setPythonDoubleVariableImpl(long contextID, String variableName, double value); private static native void setPythonStringVariableImpl(long contextID, String variableName, String value); + private static native void setPythonBooleanArrayVariableImpl(long contextID, String variableName, boolean[] value); private static native void setPythonIntegerArrayVariableImpl(long contextID, String variableName, int[] value); + private static native void setPythonLongArrayVariableImpl(long contextID, String variableName, long[] value); private static native void setPythonDoubleArrayVariableImpl(long contextID, String variableName, double[] value); private static native void setPythonStringArrayVariableImpl(long contextID, String variableName, String[] value); - private static native int getPythonIntegerVariableImpl(long contextID, String variableName); + private static native boolean getPythonBooleanVariableImpl(long contextID, String variableName); + private static native long getPythonLongVariableImpl(long contextID, String variableName); private static native double getPythonDoubleVariableImpl(long contextID, String variableName); private static native String getPythonStringVariableImpl(long contextID, String variableName); + private static native boolean[] getPythonBooleanArrayVariableImpl(long contextID, String variableName); + private static native long[] getPythonLongArrayVariableImpl(long contextID, String variableName); private static native int[] getPythonIntegerArrayVariableImpl(long contextID, String variableName); private static native double[] getPythonDoubleArrayVariableImpl(long contextID, String variableName); private static native String[] getPythonStringArrayVariableImpl(long contextID, String variableName); private static native void setPythonNDArrayVariableImpl(long contextID, String variableName, NDArray value); private static native NDArray getPythonNDArrayVariableImpl(long contextID, String variableName); + + private static native void setPythonVariantVariableImpl(long contextID, String variableName, Object value, Binding binding); + private static native Object getPythonVariantVariableImpl(long contextID, String variableName); + + private static native int getPythonVariableTypeImpl(long contextID, String variableName); + + private static native String[] getPythonVariableNamesImpl(long contextID); } diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNode.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNode.java new file mode 100644 index 0000000..c7e9df5 --- /dev/null +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNode.java @@ -0,0 +1,14 @@ +package org.simantics.pythonlink.variable; + +public class PythonNode { + public PythonNode(String variableName) { + super(); + this.variableName = variableName; + } + + private String variableName; + + public String getName() { + return variableName; + } +} diff --git a/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java b/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java new file mode 100644 index 0000000..b88ebfa --- /dev/null +++ b/org.simantics.pythonlink/src/org/simantics/pythonlink/variable/PythonNodeManager.java @@ -0,0 +1,281 @@ +package org.simantics.pythonlink.variable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.simantics.databoard.Bindings; +import org.simantics.databoard.Datatypes; +import org.simantics.databoard.adapter.AdaptException; +import org.simantics.databoard.binding.Binding; +import org.simantics.databoard.binding.error.BindingException; +import org.simantics.databoard.binding.mutable.Variant; +import org.simantics.databoard.type.Datatype; +import org.simantics.databoard.type.MapType; +import org.simantics.databoard.type.RecordType; +import org.simantics.pythonlink.NDArray; +import org.simantics.pythonlink.PythonContext; +import org.simantics.pythonlink.PythonContext.Listener; +import org.simantics.pythonlink.PythonContext.VariableType; +import org.simantics.simulator.variable.Realm; +import org.simantics.simulator.variable.exceptions.NodeManagerException; +import org.simantics.simulator.variable.impl.AbstractNodeManager; + +public class PythonNodeManager extends AbstractNodeManager { + PythonContext context; + + static Realm realm = new Realm() { + public void asyncExec(Runnable runnable) { + runnable.run(); + }; + + public void syncExec(Runnable runnable) { + runnable.run(); + }; + }; + + Map> listeners = new HashMap<>(); + Map nodes = new HashMap<>(); + + PythonNode root = new PythonNode("/"); + + static Pattern namePattern = Pattern.compile("/?([a-zA-Z_][a-zA-Z_0-9]*)"); + + static MapType dictionaryType = new MapType(Datatypes.VARIANT, Datatypes.VARIANT); + static RecordType ndarrayType = (RecordType)Bindings.getBindingUnchecked(NDArray.class).type(); + + public PythonNodeManager(PythonContext context) { + super(); + this.context = context; + + context.addListener(new Listener() { + @Override + public void updated(String variableName) { + if (variableName != null) { + Set lis = listeners.get(variableName); + if (lis != null) { + for (Runnable r : lis) + r.run(); + } + lis = listeners.get("/"); + if (lis != null) { + for (Runnable r : lis) + r.run(); + } + } + else { + Set allRunnables = new HashSet<>(); + for (Collection s : listeners.values()) + allRunnables.addAll(s); + for (Runnable r : allRunnables) + r.run(); + } + } + + @Override + public void closed() { + nodes.clear(); + Set allRunnables = new HashSet<>(); + for (Collection s : listeners.values()) + allRunnables.addAll(s); + for (Runnable r : allRunnables) + r.run(); + } + }); + } + + @Override + public Realm getRealm() { + return realm; + } + + @Override + public String getName(PythonNode node) { + return node.getName(); + } + + @Override + public void addNodeListener(PythonNode node, Runnable listener) { + synchronized(listeners) { + if (!listeners.containsKey(node.getName())) listeners.put(node.getName(), new HashSet<>()); + listeners.get(node.getName()).add(listener); + } + } + + @Override + public void removeNodeListener(PythonNode node, Runnable listener) { + synchronized(listeners) { + if (!listeners.containsKey(node.getName())) + listeners.get(node.getName()).remove(listener); + } + } + + @Override + public PythonNode getNode(String path) throws NodeManagerException { + if (!context.isOpen()) + return null; + + if ("/".equals(path)) + return root; + + Matcher match = namePattern.matcher(path); + if (!match.matches()) + return null; + + String name = match.group(1); + + if (nodes.containsKey(name)) + return nodes.get(name); + + VariableType type = context.getPythonVariableType(name); + if (type == VariableType.NO_VARIABLE) + return null; + + PythonNode node = new PythonNode(name); + nodes.put(path, node); + + return node; + } + + @Override + public PythonNode getChild(PythonNode node, String name) throws NodeManagerException { + return null; + } + + @Override + public PythonNode getProperty(PythonNode node, String name) throws NodeManagerException { + if (node != root || !context.isOpen()) return null; + + return getNode(name); + } + + @Override + public List getChildNames(PythonNode node) throws NodeManagerException { + return Collections.emptyList(); + } + + @Override + public List getChildren(PythonNode node) throws NodeManagerException { + return Collections.emptyList(); + } + + @Override + public List getPropertyNames(PythonNode node) throws NodeManagerException { + if (node != root || !context.isOpen()) return Collections.emptyList(); + return Arrays.asList(context.getPythonVariableNames()); + } + + @Override + public List getProperties(PythonNode node) throws NodeManagerException { + if (node != root || !context.isOpen()) return Collections.emptyList(); + + String[] names = context.getPythonVariableNames(); + List result = new ArrayList<>(names.length); + + for (String name : names) { + result.add(getNode(name)); + } + + return result; + } + + @Override + public Datatype getDatatype(PythonNode node) throws NodeManagerException { + String name = node.getName(); + VariableType type; + try { + type = context.getPythonVariableType(name); + } catch (RuntimeException e) { + throw new NodeManagerException("Failed to get type of variable " + name, e); + } + + switch (type) { + case NO_VARIABLE: return Datatypes.VOID; + case BOOLEAN: return Datatypes.BOOLEAN; + case LONG: return Datatypes.LONG; + case FLOAT: return Datatypes.DOUBLE; + case STRING: return Datatypes.STRING; + case BYTEARRAY: return Datatypes.BYTE_ARRAY; + case DICTIONARY: return dictionaryType; + case NDARRAY: return ndarrayType; + case SEQUENCE: return Datatypes.VARIANT_ARRAY; + case UNKNOWN: return Datatypes.VOID; + default: throw new RuntimeException("Unknown python variable type"); + } + } + + @Override + public Variant getValue(PythonNode node, String propertyName) throws NodeManagerException { + if (node == root && context.isOpen()) + return context.getPythonVariantVariable(propertyName); + + throw new NodeManagerException("No value available for " + node.getName() + "#" + propertyName); + } + + @Override + public Object getValue(PythonNode node, String propertyName, Binding binding) + throws NodeManagerException, BindingException { + Variant value = getValue(node, propertyName); + + try { + return value.getValue(binding); + } catch (AdaptException e) { + throw new BindingException(e); + } + } + + @Override + public Variant getValue(PythonNode node) throws NodeManagerException { + if (node == root || !context.isOpen()) + throw new NodeManagerException("No value available for " + node.getName()); + + String name = node.getName(); + + try { + return context.getPythonVariantVariable(name); + } catch (RuntimeException e) { + throw new NodeManagerException("Failed to get value of variable " + name, e); + } + } + + @Override + public Object getValue(PythonNode node, Binding binding) throws NodeManagerException, BindingException { + Variant value = getValue(node); + + try { + return value.getValue(binding); + } catch (AdaptException e) { + throw new BindingException(e); + } + } + + @Override + public void setValue(PythonNode node, String propertyName, Object value, Binding binding) + throws NodeManagerException, BindingException { + if (node != root || !context.isOpen()) throw new NodeManagerException("No property " + node.getName() + "#" + propertyName); + + context.setPythonVariantVariable(propertyName, value, binding); + } + + @Override + public void setValue(PythonNode node, Object value, Binding binding) throws NodeManagerException, BindingException { + if (node == root || !context.isOpen()) throw new NodeManagerException("No property " + node.getName()); + String name = node.getName(); + + context.setPythonVariantVariable(name, value, binding); + } + + @Override + public Set getClassifications(PythonNode node) throws NodeManagerException { + return Collections.emptySet(); + } + +} diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/AllTests.java b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/AllTests.java index 3d9bbb2..8778150 100644 --- a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/AllTests.java +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/AllTests.java @@ -5,7 +5,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith( Suite.class ) -@SuiteClasses( { ScriptTests.class, TestPythonlink.class } ) +@SuiteClasses( { ScriptTests.class, TestPythonlink.class, TestPythonVariable.class } ) public class AllTests { } diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/ScriptTests.java b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/ScriptTests.java index 6a8bdae..22f81fe 100644 --- a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/ScriptTests.java +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/ScriptTests.java @@ -1,12 +1,37 @@ package org.simantics.pythonlink.test; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; +import org.simantics.PlatformException; +import org.simantics.Simantics; +import org.simantics.application.arguments.Arguments; +import org.simantics.pythonlink.PythonContext; public class ScriptTests extends ScriptTestBase { + static IProgressMonitor progress = new NullProgressMonitor(); + PythonContext python; + + @BeforeClass + public static void SetUpClass() throws PlatformException { + Simantics.startUpHeadless(Arguments.parse(new String[] {}), progress); + } + + @AfterClass + public static void TearDownClass() throws PlatformException { + Simantics.shutdown(progress); + } + public ScriptTests() { super("scripts"); } @Test public void Python() throws Exception { test(); } + @Test public void Matplotlib() throws Exception { test(); } + @Test public void NDArray() throws Exception { test(); } + @Test public void Variant() throws Exception { test(); } + @Test public void Variable() throws Exception { test(); } } diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/TestPythonVariable.java b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/TestPythonVariable.java new file mode 100644 index 0000000..56ff8e8 --- /dev/null +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/TestPythonVariable.java @@ -0,0 +1,139 @@ +package org.simantics.pythonlink.test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.simantics.PlatformException; +import org.simantics.Simantics; +import org.simantics.application.arguments.Arguments; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.Datatypes; +import org.simantics.databoard.type.MapType; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.db.request.Read; +import org.simantics.pythonlink.Python; +import org.simantics.pythonlink.PythonContext; + +public class TestPythonVariable { + static IProgressMonitor progress = new NullProgressMonitor(); + + PythonContext python; + + @BeforeClass + public static void SetUpClass() throws PlatformException { + Simantics.startUpHeadless(Arguments.parse(new String[] {}), progress); + } + + @AfterClass + public static void TearDownClass() throws PlatformException { + Simantics.shutdown(progress); + } + + @Before + public void setUp() throws Exception { + python = Python.openPythonContext(); + } + + @After + public void tearDown() throws Exception { + python.close(); + } + + @Test + public void test() throws DatabaseException { + Simantics.getSession().syncRequest(new Read() { + @Override + public Void perform(ReadGraph graph) throws DatabaseException { + python.setPythonDoubleArrayVariable("foo", new double[] { 1, 2, 3 }); + + Variable var = Python.getPythonContextVariable(python); + + Object value = var.getPossiblePropertyValue(graph, "foo"); + + assertNotNull(value); + assertTrue(value instanceof Object[]); + assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[])value); + + return null; + } + }); + } + + @Test + public void test2() throws DatabaseException { + Simantics.getSession().syncRequest(new Read() { + @Override + public Void perform(ReadGraph graph) throws DatabaseException { + python.setPythonDoubleArrayVariable("foo", new double[] { 1, 2, 3 }); + + Variable var = Python.getPythonContextVariable(python); + + Object value = var.getPossiblePropertyValue(graph, "foo", Bindings.DOUBLE_ARRAY); + + assertNotNull(value); + assertTrue(value instanceof double[]); + assertArrayEquals(new double[] {1.0, 2.0, 3.0}, (double[])value, 0.0); + + return null; + } + }); + } + + @Test + public void test3() throws DatabaseException { + Simantics.getSession().syncRequest(new Read() { + @Override + public Void perform(ReadGraph graph) throws DatabaseException { + python.executePythonStatement("foo = (1, 2, 3)"); + + Variable var = Python.getPythonContextVariable(python); + Variable foo = var.browse(graph, "#foo"); + + Object value = foo.getPossibleValue(graph, Bindings.LONG_ARRAY); + + assertNotNull(value); + assertTrue(value instanceof long[]); + assertArrayEquals(new long[] {1,2,3}, (long[])value); + + return null; + } + }); + } + + @Test + public void test4() throws DatabaseException { + Simantics.getSession().syncRequest(new Read() { + @Override + public Void perform(ReadGraph graph) throws DatabaseException { + python.executePythonStatement("foo = {1:'foo', 2:'bar'}"); + + Variable var = Python.getPythonContextVariable(python); + Variable foo = var.getPossibleProperty(graph, "foo"); + + Object value = foo.getPossibleValue(graph, Bindings.getBinding(new MapType(Datatypes.LONG, Datatypes.STRING))); + + assertNotNull(value); + assertTrue(value instanceof Map); + assertEquals(2, ((Map)value).size()); + assertEquals("foo", ((Map)value).get(1L)); + assertEquals("bar", ((Map)value).get(2L)); + + return null; + } + }); + } + +} diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Matplotlib.sts b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Matplotlib.sts new file mode 100644 index 0000000..ecff4df --- /dev/null +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Matplotlib.sts @@ -0,0 +1,28 @@ +> import "Python" +> +> code = """ +> import matplotlib +> import matplotlib.pyplot as plt +> import numpy as np +> import io +> +> x, y = np.random.randn(2, 100) +> fig = plt.figure() +> ax1 = fig.add_subplot(211) +> ax1.xcorr(x, y, usevlines=True, maxlags=50, normed=True, lw=2) +> ax1.grid(True) +> ax1.axhline(0, color='black', lw=2) +> +> ax2 = fig.add_subplot(212, sharex=ax1) +> ax2.acorr(x, usevlines=True, normed=True, maxlags=50, lw=2) +> ax2.grid(True) +> ax2.axhline(0, color='black', lw=2) +> +> imgdata = io.StringIO() +> fig.savefig(imgdata, format='svg') +> svg_data = imgdata.getvalue()[0:5] +> """ +> runPython do +> executePythonStatement code +> getPythonStringVariable "svg_data" +" import "Python" +> ndarray (vector [1, 2, 3]) +ndarray(3) [1.0, 2.0, 3.0] +> ndarrayM 2 2 (vector [1, 2, 3, 4]) +ndarray(2x2) [[1.0, 2.0], [3.0, 4.0]] +> a = ndarrayN (vector [2, 2, 3]) (vector [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) +> a +ndarray(2x2x3) [[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]] +> ndarrayDims a +vector [2, 2, 3] +> ndarrayValues a +vector [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0] +> ndarrayElement a 5 +6.0 +> ndarrayElementN a (vector [0, 1, 2]) +6.0 +> b = ndarrayFromList ([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]] :: [[[Double]]]) +> b +ndarray(2x2x3) [[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]] +> runPython do +> setPythonNDArrayVariable "foo" (ndarrayM 2 3 (vector [1, 2, 3, 4, 5, 6])) +> executePythonStatement "bar = foo.cumsum(1)" +> getPythonNDArrayVariable "bar" +ndarray(2x3) [[1.0, 3.0, 6.0], [4.0, 9.0, 15.0]] diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Python.sts b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Python.sts index 50c0423..83b2212 100644 --- a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Python.sts +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Python.sts @@ -5,6 +5,9 @@ > getPythonStringArrayVariable "d" vector ["__builtins__", "__doc__", "__loader__", "__name__", "__package__", "__spec__", "foo"] > runPython do +> getPythonStringVariable "__name__" +"SCL_2" +> runPython do > setPythonDoubleVariable "foo" 1 > executePythonStatement "foo = foo + 1" > getPythonDoubleVariable "foo" @@ -25,28 +28,21 @@ vector [1.0, 2.0, 3.0, 4.0] > executePythonStatement "foo.append(sum(foo))" > getPythonIntegerArrayVariable "foo" vector [1, 2, 3, 4, 10] -> ndarray (vector [1, 2, 3]) -ndarray(3) [1.0, 2.0, 3.0] -> ndarrayM 2 2 (vector [1, 2, 3, 4]) -ndarray(2x2) [[1.0, 2.0], [3.0, 4.0]] -> a = ndarrayN (vector [2, 2, 3]) (vector [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) -> a -ndarray(2x2x3) [[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]] -> ndarrayDims a -vector [2, 2, 3] -> ndarrayValues a -vector [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0] -> ndarrayElement a 5 -6.0 -> ndarrayElementN a (vector [0, 1, 2]) -6.0 -> runPython do -> setPythonNDArrayVariable "foo" (ndarrayM 2 3 (vector [1, 2, 3, 4, 5, 6])) -> executePythonStatement "bar = foo.cumsum(1)" -> getPythonNDArrayVariable "bar" -ndarray(2x3) [[1.0, 3.0, 6.0], [4.0, 9.0, 15.0]] +> runPython do +> setPythonBooleanVariable "foo" False +> executePythonStatement "foo = not(foo)" +> getPythonBooleanVariable "foo" +True > runPython do > setPythonVariable "foo" ([1.0, 2.0, 3.0, 4.0] :: [Double]) > executePythonStatement "foo.append(sum(foo))" > getPythonVariable "foo" :: [Double] [1.0, 2.0, 3.0, 4.0, 10.0] +> py = openPythonContext +> runWithPythonContext py $ setPythonIntegerVariable "foo" 4 +> runWithPythonContext py $ setPythonDoubleArrayVariable "bar" (vector [1.0, 2.0, 3.0]) +> runWithPythonContext py $ executePythonStatement "bar.append(float(foo))" +> bar = runWithPythonContext py $ getPythonDoubleArrayVariable "bar" +> closePythonContext py +> bar +vector [1.0, 2.0, 3.0, 4.0] diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variable.sts b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variable.sts new file mode 100644 index 0000000..a735dde --- /dev/null +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variable.sts @@ -0,0 +1,35 @@ +> import "PythonVariable" +> import "Simantics/DB" +> import "String" +> +> py = openPythonContext +> runWithPythonContext py do +> setPythonIntegerVariable "foo" 4 +> setPythonDoubleArrayVariable "bar" (vector [1.0, 2.0, 3.0]) +> +> var = getPythonContextVariable py +> sort $ map nameOf $ properties var +["__builtins__", "__doc__", "__loader__", "__name__", "__package__", "__spec__", "bar", "foo"] +> foo = property var "foo" +> bar = property var "bar" +> datatype foo +Long +> datatype bar +Variant[] +> value bar :: Variant +[1.0 : Double, 2.0 : Double, 3.0 : Double] : Variant[] +> value bar :: [Double] +[1.0, 2.0, 3.0] +> possibleChild var "foo" +Nothing +> possibleProperty var "dummy" +Nothing +> runWithPythonContext py do +> executePythonStatement "baz = (1.0, 'string_value', [2.0, 3.0])" +> sort $ map nameOf $ properties var +["__builtins__", "__doc__", "__loader__", "__name__", "__package__", "__spec__", "bar", "baz", "foo"] +> v = propertyValue var "baz" :: Variant +> v +[1.0 : Double, "string_value" : String, [2.0 : Double, 3.0 : Double] : Variant[]] : Variant[] +> variantElement v 1 :: String +"string_value" diff --git a/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variant.sts b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variant.sts new file mode 100644 index 0000000..745b3c3 --- /dev/null +++ b/org.simantics.pythonlink/test/org/simantics/pythonlink/test/scripts/Variant.sts @@ -0,0 +1,22 @@ +> import "Python" +> a = variant ([1.0, 2.0, 3.0]) +> b = runPython do +> setPythonVariantVariable "foo" a +> executePythonStatement "foo.append(4.0)" +> getPythonVariantVariable "foo" +> b +[1.0 : Double, 2.0 : Double, 3.0 : Double, 4.0 : Double] : Variant[] +> variantValue b :: [Double] +[1.0, 2.0, 3.0, 4.0] +> import "MMap" as MMap +> runPython do +> foo = MMap.create () :: MMap.T String Dynamic +> MMap.put foo "c1" (toDynamic (vector [True, True, False, True])) +> MMap.put foo "c2" (toDynamic ["foo", "bar", "baz"]) +> MMap.put foo "c3" (toDynamic 25.0) +> setPythonVariantVariable "foo" (variant foo) +> executePythonStatement "c1 = foo['c1']" +> executePythonStatement "c2 = foo['c2']" +> executePythonStatement "c3 = foo['c3']" +> (getPythonBooleanArrayVariable "c1", getPythonStringArrayVariable "c2", getPythonDoubleVariable "c3") +(vector [True, True, False, True], vector ["foo", "bar", "baz"], 25.0) -- 2.45.2