diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f040a9bf4..719cd608a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- [#1141](http://github.com/nelson-lang/nelson/issues/1141) Help about Managing Data between Python and Nelson. +- Python interface (part 2): + + - [#1141](http://github.com/nelson-lang/nelson/issues/1141) Help about Managing Data between Python and Nelson. + - [#1149](https://github.com/nelson-lang/nelson/issues/1149) python bytes, and bytearray types were not managed. + - `pyenv`: can use environment variables to set values. + - `getenv`: Retrieve the values of several environment variables. -- `pyenv`: can use environment variables to set values. ### Changed @@ -21,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1144](http://github.com/nelson-lang/nelson/issues/1144) test_run markdown help file had a typo. - [#1143](http://github.com/nelson-lang/nelson/issues/1143) Linux Snapcraft version did not allow to use python. +- `single(int64([1 2; 3 4]))` returned a wrong value. +- `py.tuple`, `py.list` compatibility increased. ## 1.3.0 (2024-03-30) diff --git a/modules/python_engine/functions/@py/char.m b/modules/python_engine/functions/@py/char.m index 34b9e976b5..eeea8d9de5 100644 --- a/modules/python_engine/functions/@py/char.m +++ b/modules/python_engine/functions/@py/char.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = char(obj) - r = obj.char(); + if ismethod(obj, 'char') + r = obj.char(); + else + error([_('Wrong value for #2 argument.'), ' ', 'char']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/double.m b/modules/python_engine/functions/@py/double.m index b7b1f512a5..254adf628a 100644 --- a/modules/python_engine/functions/@py/double.m +++ b/modules/python_engine/functions/@py/double.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = double(obj) - r = obj.double(); + if ismethod(obj, 'double') + r = obj.double(); + else + error([_('Wrong value for #2 argument.'), ' ', 'double']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/int16.m b/modules/python_engine/functions/@py/int16.m index 66a3dc9dbb..ae90a4da21 100644 --- a/modules/python_engine/functions/@py/int16.m +++ b/modules/python_engine/functions/@py/int16.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = int16(obj) - r = obj.int16(); + if ismethod(obj, 'int16') + r = obj.int16(); + else + error([_('Wrong value for #2 argument.'), ' ', 'int16']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/int32.m b/modules/python_engine/functions/@py/int32.m index 16a6bc39c5..7f34e80e24 100644 --- a/modules/python_engine/functions/@py/int32.m +++ b/modules/python_engine/functions/@py/int32.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = int32(obj) - r = obj.int32(); + if ismethod(obj, 'int32') + r = obj.int32(); + else + error([_('Wrong value for #2 argument.'), ' ', 'int32']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/int64.m b/modules/python_engine/functions/@py/int64.m index e991fe476e..f0eb017ace 100644 --- a/modules/python_engine/functions/@py/int64.m +++ b/modules/python_engine/functions/@py/int64.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = int64(obj) - r = obj.int64(); + if ismethod(obj, 'int64') + r = obj.int64(); + else + error([_('Wrong value for #2 argument.'), ' ', 'int64']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/int8.m b/modules/python_engine/functions/@py/int8.m index 88c5a0dba9..6df702a047 100644 --- a/modules/python_engine/functions/@py/int8.m +++ b/modules/python_engine/functions/@py/int8.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = int8(obj) - r = obj.int8(); + if ismethod(obj, 'int8') + r = obj.int8(); + else + error([_('Wrong value for #2 argument.'), ' ', 'int8']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/logical.m b/modules/python_engine/functions/@py/logical.m index b7ebb24f08..4a17543afc 100644 --- a/modules/python_engine/functions/@py/logical.m +++ b/modules/python_engine/functions/@py/logical.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = logical(obj) - r = obj.logical(); + if ismethod(obj, 'logical') + r = obj.logical(); + else + error([_('Wrong value for #2 argument.'), ' ', 'logical']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/single.m b/modules/python_engine/functions/@py/single.m index c8d0930226..2c827a5dea 100644 --- a/modules/python_engine/functions/@py/single.m +++ b/modules/python_engine/functions/@py/single.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = single(obj) - r = obj.single(); + if ismethod(obj, 'single') + r = obj.single(); + else + error([_('Wrong value for #2 argument.'), ' ', 'single']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/string.m b/modules/python_engine/functions/@py/string.m index 425a4ea6b4..41fb4eda51 100644 --- a/modules/python_engine/functions/@py/string.m +++ b/modules/python_engine/functions/@py/string.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = string(obj) - r = obj.string(); + if ismethod(obj, 'string') + r = obj.string(); + else + error([_('Wrong value for #2 argument.'), ' ', 'string']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/uint16.m b/modules/python_engine/functions/@py/uint16.m index 18976e89b2..08479f8bd8 100644 --- a/modules/python_engine/functions/@py/uint16.m +++ b/modules/python_engine/functions/@py/uint16.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = uint16(obj) - r = obj.uint16(); + if ismethod(obj, 'uint16') + r = obj.uint16(); + else + error([_('Wrong value for #2 argument.'), ' ', 'uint16']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/uint32.m b/modules/python_engine/functions/@py/uint32.m index f5cb402b12..bfbd9035aa 100644 --- a/modules/python_engine/functions/@py/uint32.m +++ b/modules/python_engine/functions/@py/uint32.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = uint32(obj) - r = obj.uint32(); + if ismethod(obj, 'uint32') + r = obj.uint32(); + else + error([_('Wrong value for #2 argument.'), ' ', 'uint32']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/uint64.m b/modules/python_engine/functions/@py/uint64.m index b6382024e5..7f650c5e2e 100644 --- a/modules/python_engine/functions/@py/uint64.m +++ b/modules/python_engine/functions/@py/uint64.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = uint64(obj) - r = obj.uint64(); + if ismethod(obj, 'uint64') + r = obj.uint64(); + else + error([_('Wrong value for #2 argument.'), ' ', 'uint64']); + end end %============================================================================= diff --git a/modules/python_engine/functions/@py/uint8.m b/modules/python_engine/functions/@py/uint8.m index da7785d2fc..0f0be9b6e5 100644 --- a/modules/python_engine/functions/@py/uint8.m +++ b/modules/python_engine/functions/@py/uint8.m @@ -8,6 +8,10 @@ % LICENCE_BLOCK_END %============================================================================= function r = uint8(obj) - r = obj.uint8(); + if ismethod(obj, 'uint8') + r = obj.uint8(); + else + error([_('Wrong value for #2 argument.'), ' ', 'uint8']); + end end %============================================================================= diff --git a/modules/python_engine/help/en_US/xml/pyrun.xml b/modules/python_engine/help/en_US/xml/pyrun.xml index 41de0a9669..41e5160548 100644 --- a/modules/python_engine/help/en_US/xml/pyrun.xml +++ b/modules/python_engine/help/en_US/xml/pyrun.xml @@ -87,6 +87,9 @@ r = pyrun('d = a + c', 'd')]]> pyenv + + Python types supported + diff --git a/modules/python_engine/help/en_US/xml/python_types.xml b/modules/python_engine/help/en_US/xml/python_types.xml index 4dbcfeb523..015cfcf15c 100644 --- a/modules/python_engine/help/en_US/xml/python_types.xml +++ b/modules/python_engine/help/en_US/xml/python_types.xml @@ -55,6 +55,10 @@ py.bytesdouble, single, int8, uint8, int16, uint16, int32, uint32, int64, uint64, logical + + py.bytearraydouble, single, int8, uint8, int16, uint16, int32, uint32, int64, uint64, logical + py.array.arraydouble, single, int8, uint8, int16, uint16, int32, uint32, int64, uint64 diff --git a/modules/python_engine/src/cpp/PyObjectHelpers.cpp b/modules/python_engine/src/cpp/PyObjectHelpers.cpp index 6605926cb8..c2a941082c 100644 --- a/modules/python_engine/src/cpp/PyObjectHelpers.cpp +++ b/modules/python_engine/src/cpp/PyObjectHelpers.cpp @@ -12,6 +12,76 @@ //============================================================================= namespace Nelson { //============================================================================= +PythonType +getPythonType(PyObject* po) +{ + if (po == nullptr) { + return PythonType::PY_NOT_MANAGED; + } + + PyTypeObject* pyTypeObject = Py_TYPE(po); + + if (pyTypeObject == PyFloat_TypePtr) { + return PythonType::PY_FLOAT_TYPE; + } + if (pyTypeObject == PyBool_TypePtr) { + return PythonType::PY_BOOL_TYPE; + } + if (pyTypeObject == PyComplex_TypePtr) { + return PythonType::PY_COMPLEX_TYPE; + } + if (pyTypeObject == PyLong_TypePtr) { + return PythonType::PY_LONG_TYPE; + } + if (pyTypeObject == PyBytes_TypePtr) { + return PythonType::PY_BYTES_TYPE; + } + if (pyTypeObject == PyByteArray_TypePtr) { + return PythonType::PY_BYTE_ARRAY_TYPE; + } + + std::wstring typeName = TypeName(po); + if (typeName == L"NoneType") { + return PythonType::PY_NONE_TYPE; + } + if (typeName == L"memoryview") { + return PythonType::PY_MEMORY_VIEW_TYPE; + } + if (typeName == L"list") { + return PythonType::PY_LIST_TYPE; + } + if (typeName == L"tuple") { + return PythonType::PY_TUPLE_TYPE; + } + if (typeName == L"dict") { + return PythonType::PY_DICT_TYPE; + } + if (typeName == L"str") { + return PythonType::PY_STR_TYPE; + } + + std::wstring numpyPrefix = L"numpy."; + if (typeName.compare(0, numpyPrefix.length(), numpyPrefix) == 0) { + return PythonType::PY_NUMPY_TYPE; + } + + PyObject* arrayModule = NLSPyImport_ImportModule("array"); + if (arrayModule) { + PyObject* arrayType = NLSPyObject_GetAttrString(arrayModule, "array"); + if (arrayType) { + if (NLSPyObject_IsInstance(po, arrayType)) { + NLSPy_XDECREF(arrayType); + NLSPy_XDECREF(arrayModule); + return PythonType::PY_ARRAY_ARRAY_TYPE; + } + NLSPy_XDECREF(arrayType); + } + NLSPy_XDECREF(arrayModule); + } + + return PythonType::PY_NOT_MANAGED; +} +//============================================================================= std::wstring PyObjectToStringRepresentation(PyObject* po) { @@ -48,7 +118,6 @@ getPyObjectMethods(PyObject* po, bool withUnderscoreMethods) for (Py_ssize_t i = 0; i < size; ++i) { PyObject* item = NLSPyList_GetItem(dir_result, i); if (item != NULL) { - const char* attr_name = NLSPyUnicode_AsUTF8(item); if (attr_name) { PyObject* method = NLSPyObject_GetAttrString(po, attr_name); @@ -130,34 +199,17 @@ deepCopyPyObject(PyObject* obj) const char* getArrayArrayTypeCode(PyObject* pyObject) { - PyObject* arrayModule = NLSPyImport_ImportModule("array"); - if (!arrayModule) { - return nullptr; - } - PyObject* arrayType = NLSPyObject_GetAttrString(arrayModule, "array"); - if (!arrayType) { - NLSPy_XDECREF(arrayModule); - return nullptr; - } - bool isInstanceArrayArray = NLSPyObject_IsInstance(pyObject, arrayType); - if (!isInstanceArrayArray) { - NLSPy_XDECREF(arrayType); - NLSPy_XDECREF(arrayModule); - return nullptr; - } - PyObject* typeChar = NLSPyObject_GetAttrString(pyObject, "typecode"); if (typeChar) { PyObject* typeStr = NLSPyUnicode_AsUTF8String(typeChar); if (typeStr) { const char* typeCode = NLSPyBytes_AsString(typeStr); NLSPy_DECREF(typeStr); + NLSPy_XDECREF(typeChar); return typeCode; } } NLSPy_XDECREF(typeChar); - NLSPy_XDECREF(arrayType); - NLSPy_XDECREF(arrayModule); return nullptr; } @@ -252,6 +304,12 @@ PyTypecodeToNelsonType(const char* memoryViewType) if (strcmp(memoryViewType, "I") == 0) { return NLS_UINT32; } + if (strcmp(memoryViewType, "l") == 0) { + return NLS_INT32; + } + if (strcmp(memoryViewType, "L") == 0) { + return NLS_UINT32; + } if (strcmp(memoryViewType, "h") == 0) { return NLS_INT16; } @@ -264,7 +322,6 @@ PyTypecodeToNelsonType(const char* memoryViewType) if (strcmp(memoryViewType, "B") == 0) { return NLS_UINT8; } - return NLS_UNKNOWN; } //============================================================================= diff --git a/modules/python_engine/src/cpp/PyObjectHelpers.hpp b/modules/python_engine/src/cpp/PyObjectHelpers.hpp index 04fbd456a7..01f73a2b19 100644 --- a/modules/python_engine/src/cpp/PyObjectHelpers.hpp +++ b/modules/python_engine/src/cpp/PyObjectHelpers.hpp @@ -15,6 +15,28 @@ //============================================================================= namespace Nelson { //============================================================================= +enum PythonType +{ + PY_NONE_TYPE = 0, + PY_STR_TYPE, + PY_FLOAT_TYPE, + PY_BOOL_TYPE, + PY_COMPLEX_TYPE, + PY_LONG_TYPE, + PY_BYTES_TYPE, + PY_BYTE_ARRAY_TYPE, + PY_MEMORY_VIEW_TYPE, + PY_LIST_TYPE, + PY_TUPLE_TYPE, + PY_DICT_TYPE, + PY_ARRAY_ARRAY_TYPE, + PY_NUMPY_TYPE, + PY_NOT_MANAGED +}; +//============================================================================= +PythonType +getPythonType(PyObject* po); +//============================================================================= std::wstring PyObjectToStringRepresentation(PyObject* po); //============================================================================= diff --git a/modules/python_engine/src/cpp/PythonLibraryWrapper.cpp b/modules/python_engine/src/cpp/PythonLibraryWrapper.cpp index df6c75353c..fde0561eb7 100644 --- a/modules/python_engine/src/cpp/PythonLibraryWrapper.cpp +++ b/modules/python_engine/src/cpp/PythonLibraryWrapper.cpp @@ -17,6 +17,8 @@ PyTypeObject* PyBool_TypePtr = nullptr; PyTypeObject* PyComplex_TypePtr = nullptr; PyTypeObject* PyFloat_TypePtr = nullptr; PyTypeObject* PyLong_TypePtr = nullptr; +PyTypeObject* PyBytes_TypePtr = nullptr; +PyTypeObject* PyByteArray_TypePtr = nullptr; PyTypeObject* PyMemoryView_TypePtr = nullptr; PyTypeObject* PyUnicode_TypePtr = nullptr; PyTypeObject* PyDict_TypePtr = nullptr; @@ -83,7 +85,7 @@ using PROC_PyLong_FromSize_t = PyObject* (*)(size_t sz); using PROC_PyTuple_SetItem = int (*)(PyObject* p, Py_ssize_t pos, PyObject* o); using PROC_PyObject_GetBuffer = int (*)(PyObject* obj, Py_buffer* view, int flags); using PROC_PyComplex_FromCComplex = PyObject* (*)(Py_complex cplx); -using PROC_PyLong_FromLongLong = PyObject* (*)(long long); +using PROC_PyLong_FromLongLong = PyObject* (*)(long long v); using PROC_PyLong_FromUnsignedLongLong = PyObject* (*)(unsigned long long); using PROC_PyBool_FromLong = PyObject* (*)(long v); using PROC_PyLong_FromUnsignedLong = PyObject* (*)(unsigned long v); @@ -100,6 +102,8 @@ using PROC_PyEval_GetBuiltins = PyObject* (*)(void); using PROC_PyDict_GetItemString = PyObject* (*)(PyObject* dp, const char* key); using PROC_PyDict_SetItemString = int (*)(PyObject* dp, const char* key, PyObject* item); using PROC_PyDict_Keys = PyObject* (*)(PyObject* mp); +using PROC_PyBytes_AsStringAndSize = int (*)(PyObject* obj, char** s, Py_ssize_t* len); +using PROC_PyByteArray_AsString = char* (*)(PyObject* obj); //============================================================================= static std::unordered_map pythonSymbols; //============================================================================= @@ -127,15 +131,20 @@ loadPythonSymbols() LOAD_PYTHON_SYMBOL(PyComplex_Type); LOAD_PYTHON_SYMBOL(PyFloat_Type); LOAD_PYTHON_SYMBOL(PyLong_Type); + LOAD_PYTHON_SYMBOL(PyBytes_Type); + LOAD_PYTHON_SYMBOL(PyByteArray_Type); LOAD_PYTHON_SYMBOL(PyMemoryView_Type); LOAD_PYTHON_SYMBOL(PyUnicode_Type); LOAD_PYTHON_SYMBOL(PyDict_Type); LOAD_PYTHON_SYMBOL(_Py_NoneStruct); - + //============================================================================= PyBool_TypePtr = reinterpret_cast(pythonSymbols["PyBool_Type"]); PyComplex_TypePtr = reinterpret_cast(pythonSymbols["PyComplex_Type"]); PyFloat_TypePtr = reinterpret_cast(pythonSymbols["PyFloat_Type"]); PyLong_TypePtr = reinterpret_cast(pythonSymbols["PyLong_Type"]); + PyBytes_TypePtr = reinterpret_cast(pythonSymbols["PyBytes_Type"]); + PyByteArray_TypePtr = reinterpret_cast(pythonSymbols["PyByteArray_Type"]); + PyMemoryView_TypePtr = reinterpret_cast(pythonSymbols["PyMemoryView_Type"]); PyUnicode_TypePtr = reinterpret_cast(pythonSymbols["PyUnicode_Type"]); PyDict_TypePtr = reinterpret_cast(pythonSymbols["PyDict_Type"]); @@ -191,10 +200,10 @@ loadPythonSymbols() LOAD_PYTHON_SYMBOL(PyObject_GetBuffer); LOAD_PYTHON_SYMBOL(PyComplex_FromCComplex); LOAD_PYTHON_SYMBOL(PyLong_FromLongLong); + LOAD_PYTHON_SYMBOL(PyLong_FromLong); LOAD_PYTHON_SYMBOL(PyLong_FromUnsignedLongLong); LOAD_PYTHON_SYMBOL(PyBool_FromLong); LOAD_PYTHON_SYMBOL(PyLong_FromUnsignedLong); - LOAD_PYTHON_SYMBOL(PyLong_FromLong); LOAD_PYTHON_SYMBOL(PyObject_VectorcallMethod); LOAD_PYTHON_SYMBOL(PyLong_AsLongLong); LOAD_PYTHON_SYMBOL(PyLong_AsUnsignedLongLong); @@ -206,6 +215,8 @@ loadPythonSymbols() LOAD_PYTHON_SYMBOL(PyDict_GetItemString); LOAD_PYTHON_SYMBOL(PyDict_SetItemString); LOAD_PYTHON_SYMBOL(PyDict_Keys); + LOAD_PYTHON_SYMBOL(PyBytes_AsStringAndSize); + LOAD_PYTHON_SYMBOL(PyByteArray_AsString); return true; } @@ -236,6 +247,8 @@ unloadPythonLibrary() PyComplex_TypePtr = nullptr; PyFloat_TypePtr = nullptr; PyLong_TypePtr = nullptr; + PyBytes_TypePtr = nullptr; + PyByteArray_TypePtr = nullptr; PyMemoryView_TypePtr = nullptr; PyUnicode_TypePtr = nullptr; _Py_NoneStructPtr = nullptr; @@ -604,7 +617,7 @@ NLSPyLong_FromUnsignedLong(unsigned long v) PyObject* NLSPyLong_FromLong(long v) { - return reinterpret_cast(pythonSymbols["PyPyLong_FromLong"])(v); + return reinterpret_cast(pythonSymbols["PyLong_FromLong"])(v); } //============================================================================= PyObject* @@ -688,3 +701,16 @@ NLSPyDict_Keys(PyObject* mp) return reinterpret_cast(pythonSymbols["PyDict_Keys"])(mp); } //============================================================================= +int +NLSPyBytes_AsStringAndSize(PyObject* obj, char** s, Py_ssize_t* len) +{ + return reinterpret_cast(pythonSymbols["PyBytes_AsStringAndSize"])( + obj, s, len); +} +//============================================================================= +char* +NLSPyByteArray_AsString(PyObject* obj) +{ + return reinterpret_cast(pythonSymbols["PyByteArray_AsString"])(obj); +} +//============================================================================= diff --git a/modules/python_engine/src/cpp/PythonLibraryWrapper.hpp b/modules/python_engine/src/cpp/PythonLibraryWrapper.hpp index 667caf14a0..03f07b2edd 100644 --- a/modules/python_engine/src/cpp/PythonLibraryWrapper.hpp +++ b/modules/python_engine/src/cpp/PythonLibraryWrapper.hpp @@ -17,6 +17,8 @@ extern PyTypeObject* PyBool_TypePtr; extern PyTypeObject* PyComplex_TypePtr; extern PyTypeObject* PyFloat_TypePtr; extern PyTypeObject* PyLong_TypePtr; +extern PyTypeObject* PyBytes_TypePtr; +extern PyTypeObject* PyByteArray_TypePtr; extern PyTypeObject* PyMemoryView_TypePtr; extern PyTypeObject* PyUnicode_TypePtr; extern PyTypeObject* PyDict_TypePtr; @@ -27,6 +29,8 @@ extern PyObject* _Py_NoneStructPtr; #define PyComplex_Type *PyComplex_TypePtr #define PyFloat_Type *PyFloat_TypePtr #define PyLong_Type *PyLong_TypePtr +#define PyBytes_Type *PyBytes_TypePtr +#define PyByteArray_Type *PyByteArray_TypePtr #define PyMemoryView_Type *PyMemoryView_TypePtr #define PyUnicode_Type *PyUnicode_TypePtr #define PyDict_Type *PyDict_TypePtr @@ -251,3 +255,9 @@ NLSPyDict_SetItemString(PyObject* dp, const char* key, PyObject* item); PyObject* NLSPyDict_Keys(PyObject* mp); //============================================================================= +int +NLSPyBytes_AsStringAndSize(PyObject* obj, char** s, Py_ssize_t* len); +//============================================================================= +char* +NLSPyByteArray_AsString(PyObject* obj); +//============================================================================= diff --git a/modules/python_engine/src/cpp/PythonObjectHandle.cpp b/modules/python_engine/src/cpp/PythonObjectHandle.cpp index a79713f2d2..edffa88c42 100644 --- a/modules/python_engine/src/cpp/PythonObjectHandle.cpp +++ b/modules/python_engine/src/cpp/PythonObjectHandle.cpp @@ -78,19 +78,37 @@ PythonObjectHandle::display(Interface* io) std::wstring pyObjTypeName = getTypeName(); std::wstring strFormat; - if ((pyObjTypeName == L"dict") || (pyObjTypeName == L"str") || (pyObjTypeName == L"NoneType")) { + PyObject* pyObject = (PyObject*)this->getPointer(); + switch (getPythonType(pyObject)) { + case PY_DICT_TYPE: + case PY_STR_TYPE: + case PY_NONE_TYPE: { strFormat = _W(" Python %s with no properties."); - } else if ((pyObjTypeName == L"list") || (pyObjTypeName == L"int")) { + } break; + case PY_LIST_TYPE: + case PY_LONG_TYPE: { strFormat = _W(" Python %s with values:"); - } else { + } break; + case PY_FLOAT_TYPE: + case PY_BOOL_TYPE: + case PY_COMPLEX_TYPE: + case PY_BYTES_TYPE: + case PY_BYTE_ARRAY_TYPE: + case PY_MEMORY_VIEW_TYPE: + case PY_TUPLE_TYPE: + case PY_ARRAY_ARRAY_TYPE: + case PY_NUMPY_TYPE: + case PY_NOT_MANAGED: + default: { strFormat = _W(" Python %s:"); + } break; } std::wstring msg = fmt::sprintf(strFormat, pyObjTypeName); msg.append(L"\n"); msg.append(L"\n"); io->outputMessage(msg); - std::wstring rep = PyObjectToStringRepresentation(((PyObject*)this->getPointer())); + std::wstring rep = PyObjectToStringRepresentation(pyObject); io->outputMessage(std::wstring(L" ") + rep + L"\n"); } //============================================================================= @@ -123,127 +141,178 @@ PythonObjectHandle::isMethod(const std::wstring& methodName) if (pyObject) { PyObject* method = NLSPyObject_GetAttrString(pyObject, wstring_to_utf8(methodName).c_str()); bool callable = method && NLSPyCallable_Check(method); - NLSPy_DECREF(method); + if (method) { + NLSPy_DECREF(method); + } return callable; } return false; } //============================================================================= - wstringVector PythonObjectHandle::getCastMethods() { - wstringVector methodsList; + if (!methodCastNames.empty()) { + return methodCastNames; + } + + methodCastNames.push_back(L"char"); /* - methodsList.push_back(L"char"); - methodsList.push_back(L"string"); - methodsList.push_back(L"cell"); - methodsList.push_back(L"struct"); - methodsList.push_back(L"double"); - methodsList.push_back(L"single"); - methodsList.push_back(L"logical"); - methodsList.push_back(L"int8"); - methodsList.push_back(L"uint8"); - methodsList.push_back(L"int16"); - methodsList.push_back(L"uint16"); - methodsList.push_back(L"int32"); - methodsList.push_back(L"uint32"); - methodsList.push_back(L"int64"); - methodsList.push_back(L"uint64"); + methodsCastNames.push_back(L"char"); + methodsCastNames.push_back(L"string"); + methodsCastNames.push_back(L"cell"); + methodsCastNames.push_back(L"struct"); + methodsCastNames.push_back(L"double"); + methodsCastNames.push_back(L"single"); + methodsCastNames.push_back(L"logical"); + methodsCastNames.push_back(L"int8"); + methodsCastNames.push_back(L"uint8"); + methodsCastNames.push_back(L"int16"); + methodsCastNames.push_back(L"uint16"); + methodsCastNames.push_back(L"int32"); + methodsCastNames.push_back(L"uint32"); + methodsCastNames.push_back(L"int64"); + methodsCastNames.push_back(L"uint64"); */ - if (getTypeName() == L"NoneType") { - methodsList.push_back(L"char"); - } - if (getTypeName() == L"str") { - methodsList.push_back(L"numeric"); - methodsList.push_back(L"char"); - methodsList.push_back(L"string"); - methodsList.push_back(L"cell"); - } - - if (getTypeName() == L"memoryview") { - methodsList.push_back(L"char"); - methodsList.push_back(L"double"); - } - - if (getTypeName() == L"array.array") { - methodsList.push_back(L"numeric"); - methodsList.push_back(L"char"); - methodsList.push_back(L"string"); - methodsList.push_back(L"cell"); - methodsList.push_back(L"double"); - methodsList.push_back(L"single"); - methodsList.push_back(L"logical"); - methodsList.push_back(L"int8"); - methodsList.push_back(L"uint8"); - methodsList.push_back(L"int16"); - methodsList.push_back(L"uint16"); - methodsList.push_back(L"int32"); - methodsList.push_back(L"uint32"); - methodsList.push_back(L"int64"); - methodsList.push_back(L"uint64"); - } - if (getTypeName() == L"dict") { - methodsList.push_back(L"char"); - methodsList.push_back(L"struct"); - } - if (getTypeName() == L"list") { - methodsList.push_back(L"char"); - methodsList.push_back(L"string"); - methodsList.push_back(L"cell"); - methodsList.push_back(L"double"); - methodsList.push_back(L"single"); - methodsList.push_back(L"logical"); - methodsList.push_back(L"int8"); - methodsList.push_back(L"uint8"); - methodsList.push_back(L"int16"); - methodsList.push_back(L"uint16"); - methodsList.push_back(L"int32"); - methodsList.push_back(L"uint32"); - methodsList.push_back(L"int64"); - methodsList.push_back(L"uint64"); - } - if (getTypeName() == L"int") { - methodsList.push_back(L"char"); - methodsList.push_back(L"double"); - methodsList.push_back(L"single"); - methodsList.push_back(L"logical"); - methodsList.push_back(L"int8"); - methodsList.push_back(L"uint8"); - methodsList.push_back(L"int16"); - methodsList.push_back(L"uint16"); - methodsList.push_back(L"int32"); - methodsList.push_back(L"uint32"); - methodsList.push_back(L"int64"); - methodsList.push_back(L"uint64"); - } - if (getTypeName() == L"tuple") { - methodsList.push_back(L"char"); - methodsList.push_back(L"string"); - methodsList.push_back(L"cell"); - methodsList.push_back(L"double"); - methodsList.push_back(L"single"); - methodsList.push_back(L"logical"); - methodsList.push_back(L"int8"); - methodsList.push_back(L"uint8"); - methodsList.push_back(L"int16"); - methodsList.push_back(L"uint16"); - methodsList.push_back(L"int32"); - methodsList.push_back(L"uint32"); - methodsList.push_back(L"int64"); - methodsList.push_back(L"uint64"); - } - return methodsList; + PyObject* pyObject = (PyObject*)this->getPointer(); + + switch (getPythonType(pyObject)) { + case PY_STR_TYPE: { + methodCastNames.push_back(L"string"); + return methodCastNames; + + } break; + case PY_BYTES_TYPE: { + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + + } break; + + case PY_MEMORY_VIEW_TYPE: { + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"cell"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + + } break; + case PY_LIST_TYPE: { + methodCastNames.push_back(L"string"); + methodCastNames.push_back(L"cell"); + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + } break; + case PY_TUPLE_TYPE: { + methodCastNames.push_back(L"string"); + methodCastNames.push_back(L"cell"); + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + + } break; + case PY_DICT_TYPE: { + methodCastNames.push_back(L"struct"); + return methodCastNames; + } break; + case PY_ARRAY_ARRAY_TYPE: { + methodCastNames.push_back(L"numeric"); + + methodCastNames.push_back(L"string"); + methodCastNames.push_back(L"cell"); + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + } break; + case PY_BYTE_ARRAY_TYPE: + case PY_LONG_TYPE: + case PY_NUMPY_TYPE: { + methodCastNames.push_back(L"double"); + methodCastNames.push_back(L"single"); + methodCastNames.push_back(L"logical"); + methodCastNames.push_back(L"int8"); + methodCastNames.push_back(L"uint8"); + methodCastNames.push_back(L"int16"); + methodCastNames.push_back(L"uint16"); + methodCastNames.push_back(L"int32"); + methodCastNames.push_back(L"uint32"); + methodCastNames.push_back(L"int64"); + methodCastNames.push_back(L"uint64"); + return methodCastNames; + } break; + case PY_NONE_TYPE: + case PY_FLOAT_TYPE: + case PY_BOOL_TYPE: + case PY_COMPLEX_TYPE: + case PY_NOT_MANAGED: + default: { + } break; + } + return methodCastNames; } //============================================================================= bool PythonObjectHandle::isCastMethod(const std::wstring& methodName) { - wstringVector methodsList = getCastMethods(); + wstringVector methodCastNames = getCastMethods(); - auto it = std::find(methodsList.begin(), methodsList.end(), methodName); - return (it != methodsList.end()); + auto it = std::find(methodCastNames.begin(), methodCastNames.end(), methodName); + return (it != methodCastNames.end()); +} +//============================================================================= +bool +PythonObjectHandle::isPyObjectMethod(const std::wstring& methodName) +{ + PyObject* pyObject = (PyObject*)this->getPointer(); + wstringVector pythonMethodNames = getPyObjectMethods(pyObject, false); + auto it = std::find(pythonMethodNames.begin(), pythonMethodNames.end(), methodName); + return (it != pythonMethodNames.end()); } //============================================================================= wstringVector @@ -252,12 +321,13 @@ PythonObjectHandle::getMethods() if (isMainPythonInterpreter()) { return {}; } - wstringVector methodsList = getCastMethods(); + wstringVector methodCastNames = getCastMethods(); PyObject* pyObject = (PyObject*)this->getPointer(); wstringVector pythonMethodNames = getPyObjectMethods(pyObject, false); - methodsList.insert(methodsList.end(), pythonMethodNames.begin(), pythonMethodNames.end()); + methodCastNames.insert( + methodCastNames.end(), pythonMethodNames.begin(), pythonMethodNames.end()); - return methodsList; + return methodCastNames; } //============================================================================= bool @@ -320,7 +390,26 @@ PythonObjectHandle::invokeCastCellMethod(ArrayOfVector& results) Error(_W("Invalid Python object.")); } - if (getTypeName() == L"tuple") { + switch (getPythonType(pyObject)) { + case PY_LIST_TYPE: { + Py_ssize_t sz = NLSPyList_Size(pyObject); + + ArrayOf* elements = (ArrayOf*)ArrayOf::allocateArrayOf(NLS_CELL_ARRAY, sz); + Dimensions dims(1, sz); + ArrayOf res = ArrayOf(NLS_CELL_ARRAY, dims, elements); + for (Py_ssize_t i = 0; i < sz; ++i) { + PyObject* item = NLSPyList_GetItem(pyObject, i); + if (item != NULL) { + bool needDecreaseReference; + elements[i] = PyObjectToArrayOf(item, needDecreaseReference); + } else { + Error(_W("Cannot convert to ") + L"cell"); + } + } + results << res; + return true; + } break; + case PY_TUPLE_TYPE: { Py_ssize_t sz = NLSPyTuple_Size(pyObject); ArrayOf* elements = (ArrayOf*)ArrayOf::allocateArrayOf(NLS_CELL_ARRAY, sz); @@ -337,8 +426,11 @@ PythonObjectHandle::invokeCastCellMethod(ArrayOfVector& results) } results << res; return true; + } break; + default: { + Error(_W("Cannot convert to ") + L"cell"); + } break; } - Error(_W("Cannot convert to ") + L"cell"); return false; } //============================================================================= @@ -350,7 +442,8 @@ PythonObjectHandle::invokeCastStructMethod(ArrayOfVector& results) Error(_W("Invalid Python object.")); } - if (getTypeName() == L"dict") { + switch (getPythonType(pyObject)) { + case PY_DICT_TYPE: { PyObject* keys = NLSPyDict_Keys(pyObject); Py_ssize_t size = NLSPyList_Size(keys); stringVector names; @@ -370,8 +463,11 @@ PythonObjectHandle::invokeCastStructMethod(ArrayOfVector& results) } results << ArrayOf::structScalarConstructor(names, values); return true; + } break; + default: { + Error(_W("Cannot convert to ") + L"struct"); + } break; } - Error(_W("Cannot convert to ") + L"struct"); return false; } //============================================================================= @@ -382,15 +478,18 @@ PythonObjectHandle::invokeCastNumericMethod(ArrayOfVector& results) if (!pyObject) { Error(_W("Invalid Python object.")); } - - if (getTypeName() == L"memoryview") { + switch (getPythonType(pyObject)) { + case PY_MEMORY_VIEW_TYPE: { results << PyMemoryViewToArrayOf(pyObject); return true; - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - results << PyArrayArrayToArrayOf(pyObject); - return true; + } break; + case PY_ARRAY_ARRAY_TYPE: { + const char* typeCode = getArrayArrayTypeCode(pyObject); + if (typeCode) { + results << PyArrayArrayToArrayOf(pyObject); + return true; + } + } break; } return false; } @@ -402,13 +501,46 @@ PythonObjectHandle::invokeCastCharMethod(ArrayOfVector& results) if (!pyObject) { Error(_W("Invalid Python object.")); } - if (getTypeName() == L"str") { + switch (getPythonType(pyObject)) { + case PY_STR_TYPE: { std::string str = std::string(NLSPyUnicode_AsUTF8(pyObject)); results << ArrayOf::characterArrayConstructor(str); return true; + } break; + default: { + results << ArrayOf::characterArrayConstructor(PyObjectToStringRepresentation(pyObject)); + return true; + } break; } + return false; +} +//============================================================================= +template +bool +handleListOrTupleToString( + PyObject* pyObject, GetSizeFunc getSizeFunc, GetItemFunc getItemFunc, ArrayOfVector& results) +{ + Py_ssize_t sz = getSizeFunc(pyObject); + ArrayOf* elements = (ArrayOf*)ArrayOf::allocateArrayOf(NLS_STRING_ARRAY, sz); + ArrayOf res = ArrayOf(NLS_STRING_ARRAY, Dimensions(1, sz), elements); - results << ArrayOf::characterArrayConstructor(PyObjectToStringRepresentation(pyObject)); + for (Py_ssize_t i = 0; i < sz; ++i) { + PyObject* item = getItemFunc(pyObject, i); + if (item != NULL) { + if (getPythonType(item) == PY_STR_TYPE) { + std::string str = std::string(NLSPyUnicode_AsUTF8(item)); + elements[i] = ArrayOf::characterArrayConstructor(str); + } else { + elements[i] + = ArrayOf::characterArrayConstructor(PyObjectToStringRepresentation(item)); + } + } else { + Error(_W("All Python elements must be convertible as scalar to the requested type.")); + return false; + } + } + + results << res; return true; } //============================================================================= @@ -418,149 +550,326 @@ PythonObjectHandle::invokeCastStringMethod(ArrayOfVector& results) PyObject* pyObject = (PyObject*)this->getPointer(); if (!pyObject) { Error(_W("Invalid Python object.")); + return false; } - if (getTypeName() == L"str") { + + switch (getPythonType(pyObject)) { + case PY_STR_TYPE: { std::string str = std::string(NLSPyUnicode_AsUTF8(pyObject)); results << ArrayOf::stringArrayConstructor(str); return true; + } break; + case PY_LIST_TYPE: { + auto getSizeFunc = [](PyObject* obj) { return NLSPyList_Size(obj); }; + auto getItemFunc = [](PyObject* obj, Py_ssize_t i) { return NLSPyList_GetItem(obj, i); }; + return handleListOrTupleToString(pyObject, getSizeFunc, getItemFunc, results); + } break; + case PY_TUPLE_TYPE: { + auto getSizeFunc = [](PyObject* obj) { return NLSPyTuple_Size(obj); }; + auto getItemFunc = [](PyObject* obj, Py_ssize_t i) { return NLSPyTuple_GetItem(obj, i); }; + return handleListOrTupleToString(pyObject, getSizeFunc, getItemFunc, results); + } break; } return false; } //============================================================================= +template bool -PythonObjectHandle::invokeCastDoubleMethod(ArrayOfVector& results) +castRealMethod(PyObject* pyObject, NelsonType nelsonType, ArrayOfVector& results) { - PyObject* pyObject = (PyObject*)this->getPointer(); if (!pyObject) { Error(_W("Invalid Python object.")); } - if (getTypeName() == L"memoryview") { + + PythonType pyType = getPythonType(pyObject); + switch (pyType) { + case PY_NUMPY_TYPE: { + PyObject* numpy = NLSPyImport_ImportModule("numpy.core._multiarray_umath"); + // bool isarray = PyArray_Check(pyObject); + // PyArrayObject* pyArray = (PyArrayObject*)pyObject; + + ////// Get array dimensions and data pointer + // npy_intp* dims = PyArray_DIMS(pyArray); + // T* dataPtr = (T*)PyArray_DATA(pyArray); + // ArrayOf res(nelsonType, Dimensions(1, 4), dataPtr); + // results << res; + // return true; + + //// Create ArrayOf object with appropriate dimensions and data + // indexType r = dims[0]; + // indexType c = dims[0] + PyArray_NDIM(pyArray); + // Dimensions arrayDims(r, c); + // ArrayOf res(nelsonType, arrayDims, dataPtr); + // results << res; + // return true; + } break; + case PY_FLOAT_TYPE: { + double result = NLSPyFloat_AsDouble(pyObject); + ArrayOf res = ArrayOf::doubleConstructor(result); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_BOOL_TYPE: { + logical value = (logical)NLSPyObject_IsTrue(pyObject); + ArrayOf res = ArrayOf::logicalConstructor(value); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_COMPLEX_TYPE: { + std::complex value; + Py_complex pyCplx = NLSPyComplex_AsCComplex(pyObject); + ArrayOf res = ArrayOf::dcomplexConstructor(pyCplx.real, pyCplx.imag); + if (nelsonType == NLS_DOUBLE) { + res.promoteType(NLS_DCOMPLEX); + } else { + res.promoteType(NLS_SCOMPLEX); + } + results << res; + return true; + } break; + case PY_LONG_TYPE: { + double value = NLSPyLong_AsDouble(pyObject); + ArrayOf res = ArrayOf::doubleConstructor(value); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_BYTES_TYPE: { + char* buffer; + Py_ssize_t size; + if (NLSPyBytes_AsStringAndSize(pyObject, &buffer, &size) == 0) { + T* ptr = (T*)ArrayOf::allocateArrayOf(nelsonType, size); + ArrayOf res(nelsonType, Dimensions(1, size), ptr); + for (size_t k = 0; k < (size_t)size; ++k) { + ptr[k] = (T)buffer[k]; + } + results << res; + return true; + } + } break; + case PY_BYTE_ARRAY_TYPE: { + char* buffer = NLSPyByteArray_AsString(pyObject); + size_t len = strlen(buffer); + T* ptr = (T*)ArrayOf::allocateArrayOf(nelsonType, len); + ArrayOf res(nelsonType, Dimensions(1, len), ptr); + for (size_t k = 0; k < len; ++k) { + ptr[k] = (T)buffer[k]; + } + results << res; + return true; + } break; + case PY_MEMORY_VIEW_TYPE: { ArrayOf res = PyMemoryViewToArrayOf(pyObject); if (res.isComplex()) { - res.promoteType(NLS_DCOMPLEX); + if (nelsonType == NLS_DOUBLE) { + res.promoteType(NLS_DCOMPLEX); + } else { + res.promoteType(NLS_SCOMPLEX); + } } else { - res.promoteType(NLS_DOUBLE); + res.promoteType(nelsonType); } results << res; return true; - } - if (getTypeName() == L"tuple") { - Py_ssize_t sz = NLSPyTuple_Size(pyObject); - double* elements = (double*)ArrayOf::allocateArrayOf(NLS_DCOMPLEX, sz); + } break; + case PY_LIST_TYPE: + case PY_TUPLE_TYPE: { + Py_ssize_t sz + = (pyType == PY_LIST_TYPE) ? NLSPyList_Size(pyObject) : NLSPyTuple_Size(pyObject); + T* elements = (T*)ArrayOf::allocateArrayOf( + nelsonType == NLS_DOUBLE ? NLS_DCOMPLEX : NLS_SCOMPLEX, sz); Dimensions dims(1, sz); - ArrayOf res = ArrayOf(NLS_DCOMPLEX, dims, elements); + ArrayOf res + = ArrayOf(nelsonType == NLS_DOUBLE ? NLS_DCOMPLEX : NLS_SCOMPLEX, dims, elements); indexType k = 0; bool allReal = true; for (Py_ssize_t i = 0; i < sz; ++i) { - PyObject* item = NLSPyTuple_GetItem(pyObject, i); + PyObject* item = (pyType == PY_LIST_TYPE) ? NLSPyList_GetItem(pyObject, i) + : NLSPyTuple_GetItem(pyObject, i); if (item != NULL) { bool needDecreaseReference; - ArrayOf r = PyObjectToArrayOf(item, needDecreaseReference); - if (r.isNumeric()) { - if (r.isComplex()) { - std::complex c = r.getContentAsDoubleComplexScalar(); - elements[k] = c.real(); - elements[k + 1] = c.imag(); - allReal = false; + ArrayOfVector rr; + if (castRealMethod(item, NLS_DOUBLE, rr)) { + ArrayOf r = rr[0]; + if (r.isNumeric()) { + if (r.isComplex()) { + std::complex c = r.getContentAsDoubleComplexScalar(); + elements[k] = (T)c.real(); + elements[k + 1] = (T)c.imag(); + allReal = false; + } else { + elements[k] = (T)r.getContentAsDoubleScalar(); + elements[k + 1] = 0; + } + k = k + 2; } else { - elements[k] = r.getContentAsDoubleScalar(); - elements[k + 1] = 0; + Error(_W("All Python elements must be convertible as scalar to the " + "requested " + "type.")); + return false; } - k = k + 2; } else { + Error(_W("All Python elements must be convertible as scalar to the requested " + "type.")); return false; } } else { + Error(_W("All Python elements must be convertible as scalar to the requested " + "type.")); return false; } } if (allReal) { - res.promoteType(NLS_DOUBLE); + res.promoteType(nelsonType); } results << res; return true; - } - - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - if (res.isComplex()) { - res.promoteType(NLS_DCOMPLEX); - } else { - res.promoteType(NLS_DOUBLE); + } break; + case PY_ARRAY_ARRAY_TYPE: { + const char* typeCode = getArrayArrayTypeCode(pyObject); + if (typeCode) { + ArrayOf res = PyArrayArrayToArrayOf(pyObject); + if (res.isComplex()) { + if (nelsonType == NLS_DOUBLE) { + res.promoteType(NLS_DCOMPLEX); + } else { + res.promoteType(NLS_SCOMPLEX); + } + } else { + res.promoteType(nelsonType); + } + results << res; + return true; } - results << res; - return true; + } break; } return false; } //============================================================================= bool +PythonObjectHandle::invokeCastDoubleMethod(ArrayOfVector& results) +{ + PyObject* pyObject = (PyObject*)this->getPointer(); + return castRealMethod(pyObject, NLS_DOUBLE, results); +} +//============================================================================= +bool PythonObjectHandle::invokeCastSingleMethod(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); + return castRealMethod(pyObject, NLS_SINGLE, results); +} +//============================================================================= +template +bool +castIntegerMethod(PyObject* pyObject, NelsonType nelsonType, ArrayOfVector& results) +{ if (!pyObject) { Error(_W("Invalid Python object.")); } - - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - if (res.isComplex()) { - res.promoteType(NLS_SCOMPLEX); - } else { - res.promoteType(NLS_SINGLE); - } + PythonType pyType = getPythonType(pyObject); + switch (pyType) { + case PY_FLOAT_TYPE: { + double result = NLSPyFloat_AsDouble(pyObject); + ArrayOf res = ArrayOf::doubleConstructor(result); + res.promoteType(nelsonType); results << res; return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - if (res.isComplex()) { - res.promoteType(NLS_DCOMPLEX); - } else { - res.promoteType(NLS_DOUBLE); + } break; + case PY_BOOL_TYPE: { + logical value = (logical)NLSPyObject_IsTrue(pyObject); + ArrayOf res = ArrayOf::logicalConstructor(value); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_COMPLEX_TYPE: { + std::complex value; + Py_complex pyCplx = NLSPyComplex_AsCComplex(pyObject); + ArrayOf res = ArrayOf::dcomplexConstructor(pyCplx.real, pyCplx.imag); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_LONG_TYPE: { + double value = NLSPyLong_AsDouble(pyObject); + ArrayOf res = ArrayOf::doubleConstructor(value); + res.promoteType(nelsonType); + results << res; + return true; + } break; + case PY_BYTES_TYPE: { + char* buffer; + Py_ssize_t size; + if (NLSPyBytes_AsStringAndSize(pyObject, &buffer, &size) == 0) { + T* ptr = (T*)ArrayOf::allocateArrayOf(nelsonType, size); + ArrayOf res(nelsonType, Dimensions(1, size), ptr); + for (size_t k = 0; k < (size_t)size; ++k) { + ptr[k] = (T)buffer[k]; + } + results << res; + return true; + } + } break; + case PY_BYTE_ARRAY_TYPE: { + char* buffer = NLSPyByteArray_AsString(pyObject); + size_t len = strlen(buffer); + T* ptr = (T*)ArrayOf::allocateArrayOf(nelsonType, len); + ArrayOf res(nelsonType, Dimensions(1, len), ptr); + for (size_t k = 0; k < len; ++k) { + ptr[k] = (T)buffer[k]; } results << res; return true; - } - if (getTypeName() == L"tuple") { - Py_ssize_t sz = NLSPyTuple_Size(pyObject); - double* elements = (double*)ArrayOf::allocateArrayOf(NLS_SCOMPLEX, sz); - Dimensions dims(1, sz); - ArrayOf res = ArrayOf(NLS_SCOMPLEX, dims, elements); - indexType k = 0; - bool allReal = true; + } break; + case PY_ARRAY_ARRAY_TYPE: { + const char* typeCode = getArrayArrayTypeCode(pyObject); + if (typeCode) { + ArrayOf res = PyArrayArrayToArrayOf(pyObject); + res.promoteType(nelsonType); + results << res; + return true; + } + } break; + case PY_LIST_TYPE: + case PY_TUPLE_TYPE: { + Py_ssize_t sz + = (pyType == PY_LIST_TYPE) ? NLSPyList_Size(pyObject) : NLSPyTuple_Size(pyObject); + T* values = (T*)ArrayOf::allocateArrayOf(nelsonType, sz); + ArrayOf res = ArrayOf(nelsonType, Dimensions(1, sz), values); for (Py_ssize_t i = 0; i < sz; ++i) { - PyObject* item = NLSPyTuple_GetItem(pyObject, i); + PyObject* item = (pyType == PY_LIST_TYPE) ? NLSPyList_GetItem(pyObject, i) + : NLSPyTuple_GetItem(pyObject, i); if (item != NULL) { - bool needDecreaseReference; - ArrayOf r = PyObjectToArrayOf(item, needDecreaseReference); - if (r.isNumeric()) { - if (r.isComplex()) { - std::complex c = r.getContentAsSingleComplexScalar(); - elements[k] = c.real(); - elements[k + 1] = c.imag(); - allReal = false; - } else { - elements[k] = r.getContentAsSingleScalar(); - elements[k + 1] = 0; - } - k = k + 2; + ArrayOfVector rr; + if (castIntegerMethod(item, nelsonType, rr)) { + T* ptr = (T*)rr[0].getDataPointer(); + values[i] = ptr[0]; } else { + Error(_W("All Python elements must be convertible as scalar to the " + "requested " + "type.")); return false; } } else { + Error(_W("All Python elements must be convertible as scalar to the " + "requested " + "type.")); return false; } } - if (allReal) { - res.promoteType(NLS_SINGLE); - } results << res; return true; + } break; + case PY_MEMORY_VIEW_TYPE: { + ArrayOf res = PyMemoryViewToArrayOf(pyObject); + res.promoteType(nelsonType); + results << res; + return true; + } break; } - return false; } //============================================================================= @@ -568,210 +877,65 @@ bool PythonObjectHandle::invokeCastLogicalMethod(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_LOGICAL); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_LOGICAL); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_LOGICAL, results); } //============================================================================= bool PythonObjectHandle::invokeCastInt8Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_INT8); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_INT8); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_INT8, results); } //============================================================================= bool PythonObjectHandle::invokeCastInt16Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_INT16); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_INT16); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_INT16, results); } //============================================================================= bool PythonObjectHandle::invokeCastInt32Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_INT32); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_INT32); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_INT32, results); } //============================================================================= bool PythonObjectHandle::invokeCastInt64Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_INT64); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_INT64); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_INT64, results); } //============================================================================= bool PythonObjectHandle::invokeCastUInt8Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_UINT8); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_UINT8); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_UINT8, results); } //============================================================================= bool PythonObjectHandle::invokeCastUInt16Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_UINT16); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_UINT16); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_UINT16, results); } //============================================================================= bool PythonObjectHandle::invokeCastUInt32Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_UINT32); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_UINT32); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_UINT32, results); } //============================================================================= bool PythonObjectHandle::invokeCastUInt64Method(ArrayOfVector& results) { PyObject* pyObject = (PyObject*)this->getPointer(); - if (!pyObject) { - Error(_W("Invalid Python object.")); - } - const char* typeCode = getArrayArrayTypeCode(pyObject); - if (typeCode) { - ArrayOf res = PyArrayArrayToArrayOf(pyObject); - res.promoteType(NLS_UINT64); - results << res; - return true; - } - if (getTypeName() == L"memoryview") { - ArrayOf res = PyMemoryViewToArrayOf(pyObject); - res.promoteType(NLS_UINT64); - results << res; - return true; - } - return false; + return castIntegerMethod(pyObject, NLS_UINT64, results); } //============================================================================= - bool PythonObjectHandle::invokeCastMethod(const std::wstring& methodName, ArrayOfVector& results) { diff --git a/modules/python_engine/src/cpp/PythonTypesWrapper.cpp b/modules/python_engine/src/cpp/PythonTypesWrapper.cpp index 91383bf89c..62ebc30e9d 100644 --- a/modules/python_engine/src/cpp/PythonTypesWrapper.cpp +++ b/modules/python_engine/src/cpp/PythonTypesWrapper.cpp @@ -142,7 +142,7 @@ convertDoubleArray(const ArrayOf& A) } if (A.isScalar()) { pyObj = NLSPyFloat_FromDouble(A.getContentAsDoubleScalar()); - } else if (A.isEmpty() || A.isVector()) { + } else if (!A.isComplex() && (A.isEmpty() || A.isVector())) { pyObj = rowVectorToPyArray( A.getDataPointer(), A.getElementCount(), nelsonTypeToTypeCode(A.getDataClass())); } else { @@ -157,7 +157,7 @@ convertSingleArray(const ArrayOf& A) PyObject* pyObj = nullptr; if (A.isScalar()) { pyObj = NLSPyFloat_FromDouble((double)A.getContentAsSingleScalar()); - } else if (A.isEmpty() || A.isVector()) { + } else if (!A.isComplex() && (A.isEmpty() || A.isVector())) { pyObj = rowVectorToPyArray( A.getDataPointer(), A.getElementCount(), nelsonTypeToTypeCode(A.getDataClass())); } else { @@ -179,9 +179,6 @@ convertDoubleComplexArray(const ArrayOf& A) pyCplx.imag = cplx.imag(); pyCplx.real = cplx.real(); pyObj = NLSPyComplex_FromCComplex(pyCplx); - } else if (A.isEmpty() || A.isVector()) { - pyObj = rowVectorToPyArray( - A.getDataPointer(), A.getElementCount() * 2, nelsonTypeToTypeCode(A.getDataClass())); } else { pyObj = arrayToMemoryView(A.getDataPointer(), A.getDataClass(), A.getDimensions()); } @@ -198,9 +195,6 @@ convertSingleComplexArray(const ArrayOf& A) pyCplx.imag = cplx.imag(); pyCplx.real = cplx.real(); pyObj = NLSPyComplex_FromCComplex(pyCplx); - } else if (A.isEmpty() || A.isVector()) { - pyObj = rowVectorToPyArray( - A.getDataPointer(), A.getElementCount() * 2, nelsonTypeToTypeCode(A.getDataClass())); } else { pyObj = arrayToMemoryView(A.getDataPointer(), A.getDataClass(), A.getDimensions()); } @@ -372,12 +366,14 @@ rowVectorToPyArray(const void* ptrValues, size_t len, const char* dataType) for (size_t k = 0; k < len; k++) { PyObject* item = nullptr; if (!item && strcmp(dataType, "Zf") == 0) { + // not supported Py_complex pyCplx; pyCplx.real = ((const single*)ptrValues)[k]; pyCplx.imag = ((const single*)ptrValues)[k + 1]; item = NLSPyComplex_FromCComplex(pyCplx); } if (!item && strcmp(dataType, "Zd") == 0) { + // not supported Py_complex pyCplx = { ((const double*)ptrValues)[k], ((const double*)ptrValues)[k + 1] }; item = NLSPyComplex_FromCComplex(pyCplx); @@ -397,12 +393,17 @@ rowVectorToPyArray(const void* ptrValues, size_t len, const char* dataType) if (!item && strcmp(dataType, "I") == 0) { item = NLSPyLong_FromUnsignedLong((unsigned long)((const uint32*)ptrValues)[k]); } + if (!item && strcmp(dataType, "L") == 0) { + item = NLSPyLong_FromUnsignedLong((unsigned long)((const uint32*)ptrValues)[k]); + } if (!item && strcmp(dataType, "Q") == 0) { item = NLSPyLong_FromUnsignedLongLong((unsigned long long)((const uint64*)ptrValues)[k]); } if (!item && strcmp(dataType, "b") == 0) { - item = NLSPyLong_FromLong((long)((const int8*)ptrValues)[k]); + int8* ptr = (int8*)(ptrValues); + int8 i8value = ptr[k]; + item = NLSPyLong_FromLong((long)(i8value)); } if (!item && strcmp(dataType, "h") == 0) { item = NLSPyLong_FromLong((long)((const int16*)ptrValues)[k]); @@ -410,6 +411,9 @@ rowVectorToPyArray(const void* ptrValues, size_t len, const char* dataType) if (!item && strcmp(dataType, "i") == 0) { item = NLSPyLong_FromLong((long)((const int32*)ptrValues)[k]); } + if (!item && strcmp(dataType, "l") == 0) { + item = NLSPyLong_FromLong((long)((const int32*)ptrValues)[k]); + } if (!item && strcmp(dataType, "q") == 0) { item = NLSPyLong_FromLongLong((long long)((const int64*)ptrValues)[k]); } @@ -495,7 +499,7 @@ arrayToMemoryView(const void* data, NelsonType nelsonType, const Dimensions& dim } pyBuffer.ndim = (int)dims.getLength(); - Py_ssize_t* shape = new Py_ssize_t[dims.getLength()]; + Py_ssize_t* shape = (Py_ssize_t*)malloc(sizeof(Py_ssize_t) * dims.getLength()); for (size_t k = 0; k < dims.getLength(); ++k) { shape[k] = dims.getAt(k); } @@ -508,15 +512,10 @@ arrayToMemoryView(const void* data, NelsonType nelsonType, const Dimensions& dim } Py_ssize_t sz = getTypeSize(nelsonType); - - if (nelsonType == NLS_SCOMPLEX || nelsonType == NLS_DCOMPLEX) { - memcpy(ptr, data, sz * dims.getElementCount() * 2); - } else { - memcpy(ptr, data, sz * dims.getElementCount()); - } + memcpy(ptr, data, sz * dims.getElementCount()); pyBuffer.buf = ptr; - Py_ssize_t* strides = new Py_ssize_t[dims.getLength()]; + Py_ssize_t* strides = (Py_ssize_t*)malloc(sizeof(Py_ssize_t) * dims.getLength()); for (size_t k = 0; k < dims.getLength(); ++k) { strides[k] = sz; } @@ -544,40 +543,30 @@ PyMemoryViewToArrayOf(PyObject* pyObject) if (NLSPyObject_GetBuffer(pyObject, &pyBuffer, PyBUF_FORMAT | PyBUF_STRIDES) == -1) { Error(_W("Failed to get buffer from memory view.")); } + size_t nbElements = pyBuffer.len / pyBuffer.itemsize; + size_t ndim = pyBuffer.ndim; std::vector dimsVector; dimsVector.reserve(ndim); for (size_t k = 0; k < ndim; ++k) { dimsVector.push_back(pyBuffer.shape[k]); } - + if (nbElements > 0 && dimsVector.empty()) { + dimsVector.push_back(1); + dimsVector.push_back(nbElements); + } + if (dimsVector.size() == 1) { + indexType d = dimsVector[0]; + dimsVector[0] = 1; + dimsVector.push_back(d); + } Dimensions dims(dimsVector); NelsonType nelsonType = PyTypecodeToNelsonType(pyBuffer.format); switch (nelsonType) { - case NLS_DOUBLE: { - double* data = (double*)pyBuffer.buf; - double* ptr = (double*)ArrayOf::allocateArrayOf(nelsonType, dims.getElementCount()); - memcpy(ptr, data, dims.getElementCount() * sizeof(double)); - return ArrayOf(nelsonType, dims, ptr); - } break; - case NLS_SINGLE: { - single* data = (single*)pyBuffer.buf; - single* ptr = (single*)ArrayOf::allocateArrayOf(nelsonType, dims.getElementCount()); - memcpy(ptr, data, dims.getElementCount() * sizeof(single)); - return ArrayOf(nelsonType, dims, ptr); - } break; - case NLS_DCOMPLEX: { - double* data = (double*)pyBuffer.buf; - double* ptr = (double*)ArrayOf::allocateArrayOf(nelsonType, dims.getElementCount()); - memcpy(ptr, data, 2 * dims.getElementCount() * sizeof(double)); - return ArrayOf(nelsonType, dims, ptr); - } break; - case NLS_SCOMPLEX: { - single* data = (single*)pyBuffer.buf; - single* ptr = (single*)ArrayOf::allocateArrayOf(nelsonType, dims.getElementCount()); - memcpy(ptr, data, 2 * dims.getElementCount() * sizeof(single)); - return ArrayOf(nelsonType, dims, ptr); - } break; + case NLS_DCOMPLEX: + case NLS_SCOMPLEX: + case NLS_DOUBLE: + case NLS_SINGLE: case NLS_INT8: case NLS_INT16: case NLS_INT32: @@ -587,10 +576,11 @@ PyMemoryViewToArrayOf(PyObject* pyObject) case NLS_UINT32: case NLS_UINT64: case NLS_LOGICAL: { - logical* data = (logical*)pyBuffer.buf; - logical* ptr = (logical*)ArrayOf::allocateArrayOf(NLS_LOGICAL, dims.getElementCount()); - memcpy(ptr, data, dims.getElementCount() * sizeof(logical)); - return ArrayOf(NLS_LOGICAL, dims, ptr); + void* data = (void*)pyBuffer.buf; + void* ptr = (void*)ArrayOf::allocateArrayOf(nelsonType, dims.getElementCount()); + ArrayOf res = ArrayOf(nelsonType, dims, ptr); + memcpy(ptr, data, res.getByteSize()); + return res; } break; case NLS_CHAR: { } break; @@ -732,9 +722,8 @@ PyArrayArrayToDoubleArrayOf(NelsonType nelsonType, PyObject* pyObject, Py_ssize_ { Dimensions dimsVector(1, vectorSize); void* ptr = ArrayOf::allocateArrayOf(nelsonType, dimsVector.getElementCount()); - double* ptrRes = (double*)ptr; ArrayOf res = ArrayOf(nelsonType, dimsVector, ptr); - + double* ptrRes = (double*)ptr; for (Py_ssize_t i = 0; i < vectorSize; ++i) { PyObject* item = NLSPySequence_GetItem(pyObject, i); ptrRes[i] = NLSPyFloat_AsDouble(item); diff --git a/modules/python_engine/src/cpp/PythonTypesWrapper.hpp b/modules/python_engine/src/cpp/PythonTypesWrapper.hpp index f1683dee1a..d60acd66c3 100644 --- a/modules/python_engine/src/cpp/PythonTypesWrapper.hpp +++ b/modules/python_engine/src/cpp/PythonTypesWrapper.hpp @@ -27,12 +27,8 @@ arrayToMemoryView(const void* data, NelsonType nelsonType, const Dimensions& dim PyObject* arrayOfToPyObject(const ArrayOf& A); //============================================================================= -PyObject* -PyArgsArrayOfToPyObject(const ArrayOf& A); -//============================================================================= ArrayOf PyMemoryViewToArrayOf(PyObject* pyObject); //============================================================================= - } //============================================================================= diff --git a/modules/python_engine/src/include/PythonConfig.hpp b/modules/python_engine/src/include/PythonConfig.hpp index 8ecbbed6f2..e22ad9bdaf 100644 --- a/modules/python_engine/src/include/PythonConfig.hpp +++ b/modules/python_engine/src/include/PythonConfig.hpp @@ -18,4 +18,7 @@ #undef PyComplex_Type #undef PyFloat_Type #undef PyNone +#undef PyBytes_Type +#undef PyLong_Type +#undef PyByteArray_Type //============================================================================= diff --git a/modules/python_engine/src/include/PythonObjectHandle.hpp b/modules/python_engine/src/include/PythonObjectHandle.hpp index be0f825963..db0820120a 100644 --- a/modules/python_engine/src/include/PythonObjectHandle.hpp +++ b/modules/python_engine/src/include/PythonObjectHandle.hpp @@ -59,6 +59,8 @@ class NLSPYTHON_ENGINE_IMPEXP PythonObjectHandle : public HandleGenericObject isMainPythonInterpreter(); //============================================================================= private: + //============================================================================= + wstringVector methodCastNames; //============================================================================= wstringVector getCastMethods(); @@ -67,6 +69,9 @@ class NLSPYTHON_ENGINE_IMPEXP PythonObjectHandle : public HandleGenericObject isCastMethod(const std::wstring& methodName); //============================================================================= bool + isPyObjectMethod(const std::wstring& methodName); + //============================================================================= + bool invokeCastMethod(const std::wstring& methodName, ArrayOfVector& results); //============================================================================= using MethodMap = std::map>; diff --git a/modules/python_engine/tests/test_type_None.m b/modules/python_engine/tests/test_type_None.m new file mode 100644 index 0000000000..97f386abd5 --- /dev/null +++ b/modules/python_engine/tests/test_type_None.m @@ -0,0 +1,26 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('P = None', "P"); +assert_isequal(class(R), 'py.NoneType'); +assert_isequal(R.char, 'None'); +assert_checkerror('R.struct()', _('Wrong value for #2 argument. struct')); +assert_checkerror('R.cell()', _('Wrong value for #2 argument. cell')); +assert_checkerror('R.int8()', _('Wrong value for #2 argument. int8')); +assert_checkerror('R.int16()', _('Wrong value for #2 argument. int16')); +assert_checkerror('R.int32()', _('Wrong value for #2 argument. int32')); +assert_checkerror('R.int64()', _('Wrong value for #2 argument. int64')); +assert_checkerror('R.uint8()', _('Wrong value for #2 argument. uint8')); +assert_checkerror('R.uint16()', _('Wrong value for #2 argument. uint16')); +assert_checkerror('R.uint32()', _('Wrong value for #2 argument. uint32')); +assert_checkerror('R.uint64()', _('Wrong value for #2 argument. uint64')); +assert_checkerror('R.single()', _('Wrong value for #2 argument. single')); +assert_checkerror('R.double()', _('Wrong value for #2 argument. double')); diff --git a/modules/python_engine/tests/test_type_arrayarray.m b/modules/python_engine/tests/test_type_arrayarray.m new file mode 100644 index 0000000000..7a9ee22389 --- /dev/null +++ b/modules/python_engine/tests/test_type_arrayarray.m @@ -0,0 +1,167 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("", 'A', 'A', [1 2 3]); +assert_isequal(R.char(), 'array(''d'', [1.0, 2.0, 3.0])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', single([1 2 3])); +assert_isequal(R.char(), 'array(''f'', [1.0, 2.0, 3.0])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', int8([1 2 3])); +assert_isequal(R.char(), 'array(''b'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', int16([1 2 3])); +assert_isequal(R.char(), 'array(''h'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', int32([1 2 3])); +assert_isequal(R.char(), 'array(''i'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', int64([1 2 3])); +assert_isequal(R.char(), 'array(''q'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', uint8([1 2 3])); +assert_isequal(R.char(), 'array(''B'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', uint16([1 2 3])); +assert_isequal(R.char(), 'array(''H'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', uint32([1 2 3])); +assert_isequal(R.char(), 'array(''I'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', uint64([1 2 3])); +assert_isequal(R.char(), 'array(''Q'', [1, 2, 3])'); +assert_isequal(R.double(), [1 2 3]) +assert_isequal(R.single(), single([1 2 3])) +assert_isequal(R.int8(), int8([1 2 3])) +assert_isequal(R.int16(), int16([1 2 3])) +assert_isequal(R.int32(), int32([1 2 3])) +assert_isequal(R.int64(), int64([1 2 3])) +assert_isequal(R.uint8(), uint8([1 2 3])) +assert_isequal(R.uint16(), uint16([1 2 3])) +assert_isequal(R.uint32(), uint32([1 2 3])) +assert_isequal(R.uint64(), uint64([1 2 3])) +%============================================================================= +R = pyrun("", 'A', 'A', []); +assert_isequal(R.char, 'array(''d'')') +assert_isequal(R.double(), zeros(1, 0)) +assert_isequal(R.single(), single(zeros(1, 0))) +assert_isequal(R.int8(), int8(zeros(1, 0))) +assert_isequal(R.int16(), int16(zeros(1, 0))) +assert_isequal(R.int32(), int32(zeros(1, 0))) +assert_isequal(R.int64(), int64(zeros(1, 0))) +assert_isequal(R.uint8(), uint8(zeros(1, 0))) +assert_isequal(R.uint16(), uint16(zeros(1, 0))) +assert_isequal(R.uint32(), uint32(zeros(1, 0))) +assert_isequal(R.uint64(), uint64(zeros(1, 0))) +%============================================================================= +R = pyrun("", 'A', 'A', single([])); +assert_isequal(R.char, 'array(''f'')') +assert_isequal(R.double(), zeros(1, 0)) +assert_isequal(R.single(), single(zeros(1, 0))) +assert_isequal(R.int8(), int8(zeros(1, 0))) +assert_isequal(R.int16(), int16(zeros(1, 0))) +assert_isequal(R.int32(), int32(zeros(1, 0))) +assert_isequal(R.int64(), int64(zeros(1, 0))) +assert_isequal(R.uint8(), uint8(zeros(1, 0))) +assert_isequal(R.uint16(), uint16(zeros(1, 0))) +assert_isequal(R.uint32(), uint32(zeros(1, 0))) +assert_isequal(R.uint64(), uint64(zeros(1, 0))) +%============================================================================= diff --git a/modules/python_engine/tests/test_type_boolean.m b/modules/python_engine/tests/test_type_boolean.m new file mode 100644 index 0000000000..a0b1352319 --- /dev/null +++ b/modules/python_engine/tests/test_type_boolean.m @@ -0,0 +1,18 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('R = True', "R"); +assert_isequal(class(R), 'logical'); +assert_isequal(R, true); +%============================================================================= +R = pyrun('R = False', "R"); +assert_isequal(R, false); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_bytes.m b/modules/python_engine/tests/test_type_bytes.m new file mode 100644 index 0000000000..9c23696920 --- /dev/null +++ b/modules/python_engine/tests/test_type_bytes.m @@ -0,0 +1,33 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("message = 'Python is fun';byte_message = bytes(message, 'utf-8')", "byte_message"); +%============================================================================= +REF = [80 121 116 104 111 110 32 105 115 32 102 117 110]; +%============================================================================= +assert_isequal(class(R), 'py.bytes'); +assert_isequal(double(R), REF); +assert_isequal(R.double, REF); +%============================================================================= +assert_isequal(R.char(), 'b''Python is fun'''); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_isequal(R.single(), single(REF)); +assert_checkerror('R.struct()', _('Wrong value for #2 argument. struct')); +assert_checkerror('R.string()', _('Wrong value for #2 argument. string')); +%============================================================================= + diff --git a/modules/python_engine/tests/test_type_bytesarray.m b/modules/python_engine/tests/test_type_bytesarray.m new file mode 100644 index 0000000000..038e50d987 --- /dev/null +++ b/modules/python_engine/tests/test_type_bytesarray.m @@ -0,0 +1,26 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('prime_numbers = [2, 3, 5, 7]; byte_array = bytearray(prime_numbers)', "byte_array"); +assert_isequal(class(R), 'py.bytearray'); +assert_isequal(R.int8(), int8([2, 3, 5, 7])); +assert_isequal(R.int16(), int16([2, 3, 5, 7])); +assert_isequal(R.int32(), int32([2, 3, 5, 7])); +assert_isequal(R.int64(), int64([2, 3, 5, 7])); +assert_isequal(R.uint8(), uint8([2, 3, 5, 7])); +assert_isequal(R.uint16(), uint16([2, 3, 5, 7])); +assert_isequal(R.uint32(), uint32([2, 3, 5, 7])); +assert_isequal(R.uint64(), uint64([2, 3, 5, 7])); +assert_isequal(R.single(), single([2, 3, 5, 7])); +assert_isequal(R.double(), double([2, 3, 5, 7])); +assert_isequal(R.char(), 'bytearray(b''\x02\x03\x05\x07'')'); +assert_checkerror('R.struct()', _('Wrong value for #2 argument. struct')); +%============================================================================= \ No newline at end of file diff --git a/modules/python_engine/tests/test_type_complex.m b/modules/python_engine/tests/test_type_complex.m new file mode 100644 index 0000000000..417035f10a --- /dev/null +++ b/modules/python_engine/tests/test_type_complex.m @@ -0,0 +1,14 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('R = complex(3, 2)', "R"); +assert_isequal(R, 3+2i); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_dict.m b/modules/python_engine/tests/test_type_dict.m new file mode 100644 index 0000000000..3c93f9c307 --- /dev/null +++ b/modules/python_engine/tests/test_type_dict.m @@ -0,0 +1,27 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('R = {}', "R"); +assert_isequal(R.char(), '{}'); +%============================================================================= +R = pyrun("R = {'name': 'Dionysia', 'age': 28, 'location': 'Athens'}", "R"); +assert_checkerror('R.cell', _('Wrong value for #2 argument. cell')) +assert_isequal(R.char(), '{''name'': ''Dionysia'', ''age'': 28, ''location'': ''Athens''}') +%============================================================================= +st = R.struct(); +assert_isequal(fieldnames(st), {'name'; 'age'; 'location'}) +assert_isequal(class(st.name), 'py.str'); +assert_isequal(st.name.char, 'Dionysia'); +assert_isequal(class(st.age), 'py.int'); +assert_isequal(st.age.double, 28); +assert_isequal(class(st.location), 'py.str'); +assert_isequal(st.location.char, 'Athens'); +%============================================================================= \ No newline at end of file diff --git a/modules/python_engine/tests/test_type_float.m b/modules/python_engine/tests/test_type_float.m new file mode 100644 index 0000000000..236e5db302 --- /dev/null +++ b/modules/python_engine/tests/test_type_float.m @@ -0,0 +1,14 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('R = 3.2', "R"); +assert_isequal(R, 3.2); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_int.m b/modules/python_engine/tests/test_type_int.m new file mode 100644 index 0000000000..2b8edba6a3 --- /dev/null +++ b/modules/python_engine/tests/test_type_int.m @@ -0,0 +1,29 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun('P = 3', "P"); +assert_isequal(class(R), 'py.int'); +assert_isequal(R.char, '3'); +assert_isequal(R.int8(), int8(3)); +assert_isequal(R.int16(), int16(3)); +assert_isequal(R.int32(), int32(3)); +assert_isequal(R.int64(), int64(3)); +assert_isequal(R.uint8(), uint8(3)); +assert_isequal(R.uint16(), uint16(3)); +assert_isequal(R.uint32(), uint32(3)); +assert_isequal(R.uint64(), uint64(3)); +assert_isequal(R.single(), single(3)); +assert_isequal(R.double(), double(3)); +%============================================================================= +assert_checkerror('R.cell()', _('Wrong value for #2 argument. cell')); +assert_checkerror('R.struct()', _('Wrong value for #2 argument. struct')); +assert_checkerror('R.numeric()', _('Wrong value for #2 argument. numeric')); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_list.m b/modules/python_engine/tests/test_type_list.m new file mode 100644 index 0000000000..04146eb6ce --- /dev/null +++ b/modules/python_engine/tests/test_type_list.m @@ -0,0 +1,40 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("A = [1, 2, 3, 4]", 'A'); +assert_isequal(class(R), 'py.list') +RC = R.cell(); +assert_isequal(size(RC), [1, 4]) +assert_isequal(RC{1}.double(), 1); +assert_isequal(RC{2}.double(), 2); +assert_isequal(RC{3}.double(), 3); +assert_isequal(RC{4}.double(), 4); +%============================================================================= +assert_isequal(R.double(), double([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.string(), ["1", "2", "3", "4"]) +%============================================================================= +assert_isequal(R.single(), single([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.int8(), int8([1, 2, 3, 4])) +assert_isequal(R.int16(), int16([1, 2, 3, 4])) +assert_isequal(R.int32(), int32([1, 2, 3, 4])) +assert_isequal(R.int64(), int64([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.uint8(), uint8([1, 2, 3, 4])) +assert_isequal(R.uint16(), uint16([1, 2, 3, 4])) +assert_isequal(R.uint32(), uint32([1, 2, 3, 4])) +assert_isequal(R.uint64(), uint64([1, 2, 3, 4])) +%============================================================================= +R = pyrun("A = [1, '2', 3, 4]", 'A'); +assert_isequal(R.string(), ["1", "2", "3", "4"]) +assert_checkerror('R.double()', _('All Python elements must be convertible as scalar to the requested type.')) +%============================================================================= diff --git a/modules/python_engine/tests/test_type_memoryview.m b/modules/python_engine/tests/test_type_memoryview.m new file mode 100644 index 0000000000..a1b1c9a8bc --- /dev/null +++ b/modules/python_engine/tests/test_type_memoryview.m @@ -0,0 +1,166 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("", "P",'P', [1 2; 3 4]); +REF = [1 2; 3 4]; +assert_isequal(R.double, REF); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +R = pyrun("", 'A', 'A', [1 2; 3 4]); +assert_isequal(class(R), 'py.memoryview') +assert_isequal(R.format.char, 'd'); +assert_isequal(R.double, [1 2; 3 4]) +%============================================================================= +R = pyrun("", 'A', 'A', single([1 2; 3 4])); +assert_isequal(class(R), 'py.memoryview') +assert_isequal(R.format.char, 'f'); +assert_isequal(R.double, [1 2; 3 4]) +%============================================================================= + +R = pyrun("", 'A', 'A', [1 2 3]+i); +assert_isequal(class(R), 'py.memoryview') +assert_isequal(R.format.char, 'Zd'); +assert_isequal(R.double, [1 2 3]+i) +%============================================================================= +R = pyrun("", 'A', 'A', single([1 2 3]+i)); +assert_isequal(class(R), 'py.memoryview') +assert_isequal(R.format.char, 'Zf'); +assert_isequal(R.double, [1 2 3]+i) +%============================================================================= +A = int8([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = int16([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = int32([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = int64([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = uint8([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = uint16([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = uint32([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= +A = uint64([1 2; 3 4]); +R = pyrun("", "P",'P', A); +REF = A; +assert_isequal(R.double, double(REF)); +assert_isequal(R.single, single(REF)); +assert_isequal(R.int8(), int8(REF)); +assert_isequal(R.int16(), int16(REF)); +assert_isequal(R.int32(), int32(REF)); +assert_isequal(R.int64(), int64(REF)); +assert_isequal(R.uint8(), uint8(REF)); +assert_isequal(R.uint16(), uint16(REF)); +assert_isequal(R.uint32(), uint32(REF)); +assert_isequal(R.uint64(), uint64(REF)); +assert_checkerror('R.cell()', _('Cannot convert to cell')); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_str.m b/modules/python_engine/tests/test_type_str.m new file mode 100644 index 0000000000..89b6c95880 --- /dev/null +++ b/modules/python_engine/tests/test_type_str.m @@ -0,0 +1,16 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("R = 'Hello from Python'", "R"); +assert_isequal(R.char, 'Hello from Python'); +assert_isequal(R.string, "Hello from Python"); +assert_checkerror('R.struct()', _('Wrong value for #2 argument. struct')); +%============================================================================= diff --git a/modules/python_engine/tests/test_type_tuple.m b/modules/python_engine/tests/test_type_tuple.m new file mode 100644 index 0000000000..82aeea2493 --- /dev/null +++ b/modules/python_engine/tests/test_type_tuple.m @@ -0,0 +1,36 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +% <--PYTHON ENVIRONMENT REQUIRED--> +%============================================================================= +R = pyrun("A = (1, 2, 3, 4)", 'A'); +assert_isequal(class(R), 'py.tuple') +assert_isequal(R.char, '(1, 2, 3, 4)') +RC = R.cell(); +assert_isequal(size(RC), [1, 4]) +assert_isequal(RC{1}.double(), 1); +assert_isequal(RC{2}.double(), 2); +assert_isequal(RC{3}.double(), 3); +assert_isequal(RC{4}.double(), 4); +%============================================================================= +assert_isequal(R.double(), double([1, 2, 3, 4])) +assert_isequal(R.single(), single([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.int8(), int8([1, 2, 3, 4])) +assert_isequal(R.int16(), int16([1, 2, 3, 4])) +assert_isequal(R.int32(), int32([1, 2, 3, 4])) +assert_isequal(R.int64(), int64([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.uint8(), uint8([1, 2, 3, 4])) +assert_isequal(R.uint16(), uint16([1, 2, 3, 4])) +assert_isequal(R.uint32(), uint32([1, 2, 3, 4])) +assert_isequal(R.uint64(), uint64([1, 2, 3, 4])) +%============================================================================= +assert_isequal(R.string(), string([1, 2, 3, 4])) +%============================================================================= diff --git a/modules/single/src/cpp/ToSingle.cpp b/modules/single/src/cpp/ToSingle.cpp index 86e834426f..61544e60bb 100644 --- a/modules/single/src/cpp/ToSingle.cpp +++ b/modules/single/src/cpp/ToSingle.cpp @@ -69,7 +69,7 @@ ToSingle(const ArrayOf& A, bool& needToOverload) return ToSingle(A); } break; case NLS_INT64: { - return ToSingle(A); + return ToSingle(A); } break; case NLS_SCOMPLEX: case NLS_SINGLE: { diff --git a/modules/single/tests/test_single.m b/modules/single/tests/test_single.m new file mode 100644 index 0000000000..ecec67aab8 --- /dev/null +++ b/modules/single/tests/test_single.m @@ -0,0 +1,14 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +A = int64([1 2; 3 4]); +R = single(A); +REF = single([1 2; 3 4]); +assert_isequal(R, REF); +%=============================================================================