Chris@66: /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */ fazekasgy@37: /* fazekasgy@37: fazekasgy@37: * Vampy : This plugin is a wrapper around the Vamp plugin API. fazekasgy@37: * It allows for writing Vamp plugins in Python. fazekasgy@37: fazekasgy@37: * Centre for Digital Music, Queen Mary University of London. fazekasgy@37: * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources fazekasgy@37: * for licence information.) fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: #include fazekasgy@37: Chris@71: #include "PyTypeConversions.h" fazekasgy@37: fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #ifndef SIZE_T_MAX fazekasgy@37: #define SIZE_T_MAX ((size_t) -1) fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: using std::string; fazekasgy@37: using std::vector; fazekasgy@37: using std::cerr; fazekasgy@37: using std::endl; fazekasgy@37: fazekasgy@37: /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS Chris@66: (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */ fazekasgy@37: Chris@71: PyTypeConversions::PyTypeConversions() : fazekasgy@37: m_strict(false), fazekasgy@37: m_error(false), fazekasgy@51: m_numpyInstalled(false), fazekasgy@37: error(m_error) // const public reference for easy access fazekasgy@37: { fazekasgy@37: } fazekasgy@37: Chris@71: PyTypeConversions::~PyTypeConversions() fazekasgy@37: { fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// floating point numbers (TODO: check numpy.float128) fazekasgy@37: float Chris@71: PyTypeConversions::PyValue_To_Float(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // convert float fazekasgy@37: if (pyValue && PyFloat_Check(pyValue)) fazekasgy@37: //TODO: check for limits here (same on most systems) fazekasgy@37: return (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@37: fazekasgy@37: if (pyValue == NULL) fazekasgy@37: { fazekasgy@51: setValueError("Error while converting object " + PyValue_Get_TypeName(pyValue) + " to float. ",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // in strict mode we will not try harder fazekasgy@37: if (m_strict) { fazekasgy@51: setValueError("Strict conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert other objects supporting the number protocol fazekasgy@37: if (PyNumber_Check(pyValue)) fazekasgy@37: { fazekasgy@37: PyObject* pyFloat = PyNumber_Float(pyValue); // new ref fazekasgy@37: if (!pyFloat) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting " + PyValue_Get_TypeName(pyValue) + " object to float.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: float rValue = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@37: Py_DECREF(pyFloat); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: /* fazekasgy@37: // convert other objects supporting the number protocol fazekasgy@37: if (PyNumber_Check(pyValue)) fazekasgy@37: { fazekasgy@37: // PEP353: Py_ssize_t is size_t but signed ! fazekasgy@37: // This will work up to numpy.float64 fazekasgy@37: Py_ssize_t rValue = PyNumber_AsSsize_t(pyValue,NULL); fazekasgy@37: if (PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: setValueError("Error while converting integer object.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: if (rValue > (Py_ssize_t)FLT_MAX || rValue < (Py_ssize_t)FLT_MIN) fazekasgy@37: { fazekasgy@37: setValueError("Overflow error. Object can not be converted to float.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: return (float) rValue; fazekasgy@37: } fazekasgy@37: */ fazekasgy@37: // convert string fazekasgy@37: if (PyString_Check(pyValue)) fazekasgy@37: { fazekasgy@37: PyObject* pyFloat = PyFloat_FromString(pyValue,NULL); fazekasgy@37: if (!pyFloat) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: setValueError("String value can not be converted to float.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: float rValue = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@37: if (PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: Py_CLEAR(pyFloat); fazekasgy@37: setValueError("Error while converting float object.",m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: Py_DECREF(pyFloat); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert the first element of any iterable sequence (for convenience and backwards compatibility) cannam@41: if (PySequence_Check(pyValue) && PySequence_Size(pyValue) > 0) fazekasgy@37: { fazekasgy@37: PyObject* item = PySequence_GetItem(pyValue,0); fazekasgy@37: if (item) fazekasgy@37: { fazekasgy@37: float rValue = this->PyValue_To_Float(item); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(item); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(item); fazekasgy@37: std::string msg = "Could not convert sequence element to float. "; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // give up fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: std::string msg = "Conversion from " + PyValue_Get_TypeName(pyValue) + " to float is not possible."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_Float failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return 0.0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// size_t (unsigned integer types) fazekasgy@37: size_t Chris@71: PyTypeConversions::PyValue_To_Size_t(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // convert objects supporting the number protocol fazekasgy@37: if (PyNumber_Check(pyValue)) fazekasgy@37: { fazekasgy@37: if (m_strict && !PyInt_Check(pyValue) && !PyLong_Check(pyValue)) fazekasgy@37: setValueError("Strict conversion error: object is not integer type.",m_strict); fazekasgy@37: // Note: this function handles Bool,Int,Long,Float fazekasgy@37: // speed is not critical in the use of this type by Vamp fazekasgy@37: // PEP353: Py_ssize_t is size_t but signed ! fazekasgy@37: Py_ssize_t rValue = PyInt_AsSsize_t(pyValue); fazekasgy@37: if (PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: setValueError("Error while converting integer object.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } Chris@70: // this test is nonsense -- neither part can occur Chris@70: // owing to range of data types -- size_t is at least Chris@70: // as big as long, and unsigned is always non-negative Chris@70: /* fazekasgy@37: if ((unsigned long)rValue > SIZE_T_MAX || (unsigned long)rValue < 0) fazekasgy@37: { fazekasgy@37: setValueError("Overflow error. Object can not be converted to size_t.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } Chris@70: */ fazekasgy@37: return (size_t) rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // in strict mode we will not try harder and throw an exception fazekasgy@37: // then the caller should decide what to do with it fazekasgy@37: if (m_strict) { fazekasgy@37: setValueError("Strict conversion error: object is not integer.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert string fazekasgy@37: if (PyString_Check(pyValue)) fazekasgy@37: { fazekasgy@37: PyObject* pyLong = PyNumber_Long(pyValue); fazekasgy@37: if (!pyLong) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: setValueError("String object can not be converted to size_t.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: size_t rValue = this->PyValue_To_Size_t(pyLong); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(pyLong); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyLong); fazekasgy@37: setValueError ("Error converting string to size_t.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert the first element of iterable sequences cannam@41: if (PySequence_Check(pyValue) && PySequence_Size(pyValue) > 0) fazekasgy@37: { fazekasgy@37: PyObject* item = PySequence_GetItem(pyValue,0); fazekasgy@37: if (item) fazekasgy@37: { fazekasgy@37: size_t rValue = this->PyValue_To_Size_t(item); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(item); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(item); fazekasgy@37: setValueError("Could not convert sequence element to size_t. ",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // give up fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to size_t is not possible."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_Size_t failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// long and int fazekasgy@37: long Chris@71: PyTypeConversions::PyValue_To_Long(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // most common case: convert int (faster) fazekasgy@37: if (pyValue && PyInt_Check(pyValue)) { fazekasgy@37: // if the object is not NULL and verified, this macro just extracts the value. fazekasgy@37: return PyInt_AS_LONG(pyValue); fazekasgy@37: } fazekasgy@37: fazekasgy@37: // long fazekasgy@37: if (PyLong_Check(pyValue)) { fazekasgy@37: long rValue = PyLong_AsLong(pyValue); fazekasgy@37: if (PyErr_Occurred()) { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: setValueError("Error while converting long object.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: if (m_strict) { fazekasgy@37: setValueError("Strict conversion error: object is not integer or long integer.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert all objects supporting the number protocol fazekasgy@37: if (PyNumber_Check(pyValue)) fazekasgy@37: { fazekasgy@37: // Note: this function handles Bool,Int,Long,Float fazekasgy@37: // PEP353: Py_ssize_t is size_t but signed ! fazekasgy@37: Py_ssize_t rValue = PyInt_AsSsize_t(pyValue); fazekasgy@37: if (PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: setValueError("Error while converting integer object.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: if (rValue > LONG_MAX || rValue < LONG_MIN) fazekasgy@37: { fazekasgy@37: setValueError("Overflow error. Object can not be converted to size_t.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: return (long) rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert string fazekasgy@37: if (PyString_Check(pyValue)) fazekasgy@37: { fazekasgy@37: PyObject* pyLong = PyNumber_Long(pyValue); fazekasgy@37: if (!pyLong) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: setValueError("String object can not be converted to long.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: long rValue = this->PyValue_To_Long(pyLong); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(pyLong); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyLong); fazekasgy@37: setValueError ("Error converting string to long.",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert the first element of iterable sequences cannam@46: if (PySequence_Check(pyValue) && PySequence_Size(pyValue) > 0) fazekasgy@37: { fazekasgy@37: PyObject* item = PySequence_GetItem(pyValue,0); fazekasgy@37: if (item) fazekasgy@37: { fazekasgy@37: size_t rValue = this->PyValue_To_Long(item); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(item); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(item); fazekasgy@37: setValueError("Could not convert sequence element to long. ",m_strict); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // give up fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to long is not possible."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_Long failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: bool Chris@71: PyTypeConversions::PyValue_To_Bool(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // convert objects supporting the number protocol fazekasgy@37: // Note: PyBool is a subclass of PyInt fazekasgy@37: if (PyNumber_Check(pyValue)) fazekasgy@37: { fazekasgy@37: if (m_strict && !PyBool_Check(pyValue)) fazekasgy@37: setValueError fazekasgy@37: ("Strict conversion error: object is not boolean type.",m_strict); fazekasgy@37: fazekasgy@37: // Note: this function handles Bool,Int,Long,Float fazekasgy@37: Py_ssize_t rValue = PyInt_AsSsize_t(pyValue); fazekasgy@37: if (PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: setValueError ("Error while converting boolean object.",m_strict); fazekasgy@37: } fazekasgy@37: if (rValue != 1 && rValue != 0) fazekasgy@37: { fazekasgy@37: setValueError ("Overflow error. Object can not be converted to boolean.",m_strict); fazekasgy@37: } fazekasgy@37: return (bool) rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: if (m_strict) { fazekasgy@37: setValueError ("Strict conversion error: object is not numerical type.",m_strict); fazekasgy@37: return false; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert iterables: the rule is the same as in the interpreter: fazekasgy@37: // empty sequence evaluates to False, anything else is True fazekasgy@37: if (PySequence_Check(pyValue)) fazekasgy@37: { fazekasgy@37: return PySequence_Size(pyValue)?true:false; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // give up fazekasgy@37: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@37: std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to boolean is not possible."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_Bool failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return false; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// string and objects that support .__str__() fazekasgy@37: /// TODO: check unicode objects fazekasgy@37: std::string Chris@71: PyTypeConversions::PyValue_To_String(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // convert string fazekasgy@37: if (PyString_Check(pyValue)) fazekasgy@37: { fazekasgy@37: char *cstr = PyString_AS_STRING(pyValue); fazekasgy@37: if (!cstr) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting string object.",m_strict); fazekasgy@37: return std::string(); fazekasgy@37: } fazekasgy@37: return std::string(cstr); fazekasgy@37: } fazekasgy@37: // TODO: deal with unicode here (argh!) fazekasgy@37: fazekasgy@37: // in strict mode we will not try harder fazekasgy@37: if (m_strict) { fazekasgy@37: setValueError("Strict conversion error: object is not string.",m_strict); fazekasgy@37: return std::string(); fazekasgy@37: } fazekasgy@37: fazekasgy@37: // accept None as empty string fazekasgy@37: if (pyValue == Py_None) return std::string(); fazekasgy@37: fazekasgy@37: // convert list or tuple: empties are turned into empty strings conventionally fazekasgy@37: if (PyList_Check(pyValue) || PyTuple_Check(pyValue)) fazekasgy@37: { fazekasgy@37: if (!PySequence_Size(pyValue)) return std::string(); fazekasgy@37: PyObject* item = PySequence_GetItem(pyValue,0); fazekasgy@37: if (item) fazekasgy@37: { fazekasgy@37: std::string rValue = this->PyValue_To_String(item); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(item); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(item); fazekasgy@37: setValueError("Could not convert sequence element to string.",m_strict); fazekasgy@37: return std::string(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // convert any other object that has .__str__() or .__repr__() fazekasgy@37: PyObject* pyString = PyObject_Str(pyValue); fazekasgy@37: if (pyString && !PyErr_Occurred()) fazekasgy@37: { fazekasgy@37: std::string rValue = this->PyValue_To_String(pyString); fazekasgy@37: if (!m_error) { fazekasgy@37: Py_DECREF(pyString); fazekasgy@37: return rValue; fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyString); fazekasgy@37: std::string msg = "Object " + this->PyValue_Get_TypeName(pyValue) +" can not be represented as string. "; fazekasgy@37: setValueError (msg,m_strict); fazekasgy@37: return std::string(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: // give up fazekasgy@37: PyErr_Print(); PyErr_Clear(); fazekasgy@37: std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to string is not possible."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_String failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return std::string(); fazekasgy@37: } fazekasgy@37: fazekasgy@37: /* C Values to Py Values */ fazekasgy@37: fazekasgy@37: fazekasgy@37: PyObject* Chris@71: PyTypeConversions::PyValue_From_CValue(const char* cValue) const fazekasgy@37: { fazekasgy@37: // returns new reference fazekasgy@37: #ifdef _DEBUG fazekasgy@37: if (!cValue) { Chris@71: std::string msg = "PyTypeConversions::PyValue_From_CValue: Null pointer encountered while converting from const char* ."; fazekasgy@37: cerr << msg << endl; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: PyObject *pyValue = PyString_FromString(cValue); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting from char* or string.",m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_From_CValue: Interpreter failed to convert from const char*" << endl; fazekasgy@37: #endif fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: return pyValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyObject* Chris@71: PyTypeConversions::PyValue_From_CValue(size_t cValue) const fazekasgy@37: { fazekasgy@37: // returns new reference fazekasgy@37: PyObject *pyValue = PyInt_FromSsize_t((Py_ssize_t)cValue); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting from size_t.",m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_From_CValue: Interpreter failed to convert from size_t" << endl; fazekasgy@37: #endif fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: return pyValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyObject* Chris@71: PyTypeConversions::PyValue_From_CValue(double cValue) const fazekasgy@37: { fazekasgy@37: // returns new reference fazekasgy@37: PyObject *pyValue = PyFloat_FromDouble(cValue); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting from float or double.",m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_From_CValue: Interpreter failed to convert from float or double" << endl; fazekasgy@37: #endif fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: return pyValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyObject* Chris@71: PyTypeConversions::PyValue_From_CValue(bool cValue) const fazekasgy@37: { fazekasgy@37: // returns new reference fazekasgy@37: PyObject *pyValue = PyBool_FromLong((long)cValue); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: setValueError("Error while converting from bool.",m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_From_CValue: Interpreter failed to convert from bool" << endl; fazekasgy@37: #endif fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: return pyValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Sequence Types to C++ Types */ fazekasgy@37: fazekasgy@37: //convert Python list to C++ vector of strings fazekasgy@37: std::vector Chris@71: PyTypeConversions::PyValue_To_StringVector (PyObject *pyList) const fazekasgy@37: { fazekasgy@37: fazekasgy@37: std::vector Output; fazekasgy@37: std::string ListElement; fazekasgy@37: PyObject *pyString = NULL; fazekasgy@37: fazekasgy@37: if (PyList_Check(pyList)) { fazekasgy@37: fazekasgy@37: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { fazekasgy@37: //Get next list item (Borrowed Reference) fazekasgy@37: pyString = PyList_GET_ITEM(pyList,i); fazekasgy@37: ListElement = (string) PyString_AsString(PyObject_Str(pyString)); fazekasgy@37: Output.push_back(ListElement); fazekasgy@37: } fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@51: fazekasgy@51: // #ifdef _DEBUG Chris@71: // cerr << "PyTypeConversions::PyValue_To_StringVector: Warning: Value is not list of strings." << endl; fazekasgy@51: // #endif fazekasgy@37: fazekasgy@37: /// Assume a single value that can be casted as string fazekasgy@37: /// this allows to write e.g. Feature.label = 5.2 instead of ['5.2'] fazekasgy@37: Output.push_back(PyValue_To_String(pyList)); fazekasgy@37: if (m_error) { fazekasgy@37: std::string msg = "Value is not list of strings nor can be casted as string. "; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_StringVector failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: } fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@37: //convert PyFeature.value (typically a list or numpy array) to C++ vector of floats fazekasgy@37: std::vector Chris@71: PyTypeConversions::PyValue_To_FloatVector (PyObject *pyValue) const fazekasgy@37: { fazekasgy@37: fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@51: if (m_numpyInstalled) fazekasgy@51: { fazekasgy@37: // there are four types of values we may receive from a numpy process: fazekasgy@37: // * a python scalar, fazekasgy@37: // * an array scalar, (e.g. numpy.float32) fazekasgy@37: // * an array with nd = 0 (0D array) fazekasgy@37: // * an array with nd > 0 fazekasgy@37: fazekasgy@37: /// check for scalars fazekasgy@37: if (PyArray_CheckScalar(pyValue) || PyFloat_Check(pyValue)) { fazekasgy@37: fazekasgy@37: std::vector Output; fazekasgy@37: fazekasgy@37: // we rely on the behaviour the scalars are either floats fazekasgy@37: // or support the number protocol fazekasgy@37: // TODO: a potential optimisation is to handle them directly fazekasgy@37: Output.push_back(PyValue_To_Float(pyValue)); fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// numpy array fazekasgy@37: if (PyArray_CheckExact(pyValue)) fazekasgy@37: return PyArray_To_FloatVector(pyValue); fazekasgy@51: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: /// python list of floats (backward compatible) fazekasgy@37: if (PyList_Check(pyValue)) { fazekasgy@37: return PyList_To_FloatVector(pyValue); fazekasgy@37: } fazekasgy@37: fazekasgy@37: std::vector Output; fazekasgy@37: fazekasgy@37: /// finally assume a single value supporting the number protocol fazekasgy@37: /// this allows to write e.g. Feature.values = 5 instead of [5.00] fazekasgy@37: Output.push_back(PyValue_To_Float(pyValue)); fazekasgy@37: if (m_error) { fazekasgy@37: std::string msg = "Value is not list or array of floats nor can be casted as float. "; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyValue_To_FloatVector failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: } fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@37: //convert a list of python floats fazekasgy@37: std::vector Chris@71: PyTypeConversions::PyList_To_FloatVector (PyObject *inputList) const fazekasgy@37: { fazekasgy@37: std::vector Output; fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: // This is a low level function normally called from fazekasgy@37: // PyValue_To_FloatVector(). Checking for list is not required. fazekasgy@37: if (!PyList_Check(inputList)) { fazekasgy@37: std::string msg = "Value is not list."; fazekasgy@37: setValueError(msg,true); Chris@71: cerr << "PyTypeConversions::PyList_To_FloatVector failed. " << msg << endl; fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: float ListElement; fazekasgy@37: PyObject *pyFloat = NULL; fazekasgy@37: PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList); fazekasgy@37: fazekasgy@37: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { fazekasgy@37: fazekasgy@37: // pyFloat = PyList_GET_ITEM(inputList,i); fazekasgy@37: pyFloat = pyObjectArray[i]; fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: if (!pyFloat) { fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Chris@71: cerr << "PyTypeConversions::PyList_To_FloatVector: Could not obtain list element: " fazekasgy@37: << i << " PyList_GetItem returned NULL! Skipping value." << endl; fazekasgy@37: continue; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: // ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@37: ListElement = PyValue_To_Float(pyFloat); fazekasgy@37: fazekasgy@37: fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: cerr << "value: " << ListElement << endl; fazekasgy@37: #endif fazekasgy@37: Output.push_back(ListElement); fazekasgy@37: } fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@51: // if numpy is not installed this will not be called, fazekasgy@51: // therefor we do not check again fazekasgy@51: #ifdef HAVE_NUMPY fazekasgy@37: std::vector Chris@71: PyTypeConversions::PyArray_To_FloatVector (PyObject *pyValue) const fazekasgy@37: { fazekasgy@37: std::vector Output; fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: // This is a low level function, normally called from fazekasgy@37: // PyValue_To_FloatVector(). Checking the array here is not required. fazekasgy@37: if (!PyArray_Check(pyValue)) { Chris@71: std::string msg = "Object has no array conversions."; fazekasgy@37: setValueError(msg,true); Chris@71: cerr << "PyTypeConversions::PyArray_To_FloatVector failed. " << msg << endl; fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: PyArrayObject* pyArray = (PyArrayObject*) pyValue; Chris@66: PyArray_Descr* descr = PyArray_DESCR(pyArray); fazekasgy@37: fazekasgy@37: /// check raw data and descriptor pointers Chris@66: if (PyArray_DATA(pyArray) == 0 || descr == 0) { fazekasgy@37: std::string msg = "NumPy array with NULL data or descriptor pointer encountered."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyArray_To_FloatVector failed. Error: " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// check dimensions Chris@66: if (PyArray_NDIM(pyArray) != 1) { fazekasgy@37: std::string msg = "NumPy array must be a one dimensional vector."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyArray_To_FloatVector failed. Error: " << msg << " Dims: " << (int) PyArray_NDIM(pyArray) << endl; fazekasgy@37: #endif fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: fazekasgy@37: #ifdef _DEBUG_VALUES Chris@71: cerr << "PyTypeConversions::PyArray_To_FloatVector: Numpy array verified." << endl; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: /// check strides (useful if array is not continuous) Chris@66: size_t strides = *((size_t*) PyArray_STRIDES(pyArray)); fazekasgy@37: fazekasgy@37: /// convert the array fazekasgy@37: switch (descr->type_num) fazekasgy@37: { fazekasgy@37: case NPY_FLOAT : // dtype='float32' Chris@66: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); fazekasgy@37: case NPY_DOUBLE : // dtype='float64' Chris@66: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); fazekasgy@37: case NPY_INT : // dtype='int' Chris@66: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); fazekasgy@37: case NPY_LONG : // dtype='long' Chris@66: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); fazekasgy@37: default : fazekasgy@37: std::string msg = "Unsupported value type in NumPy array object."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG Chris@71: cerr << "PyTypeConversions::PyArray_To_FloatVector failed. Error: " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return Output; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Error handling */ fazekasgy@37: fazekasgy@37: void Chris@71: PyTypeConversions::setValueError (std::string message, bool strict) const fazekasgy@37: { fazekasgy@37: m_error = true; fazekasgy@37: m_errorQueue.push(ValueError(message,strict)); fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// return a reference to the last error or creates a new one. Chris@71: ValueError& Chris@71: PyTypeConversions::lastError() const fazekasgy@37: { fazekasgy@37: m_error = false; fazekasgy@37: if (!m_errorQueue.empty()) return m_errorQueue.back(); fazekasgy@37: else { fazekasgy@37: m_errorQueue.push(ValueError("Type conversion error.",m_strict)); fazekasgy@37: return m_errorQueue.back(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// helper function to iterate over the error message queue: fazekasgy@37: /// pops the oldest item Chris@71: ValueError Chris@71: PyTypeConversions::getError() const fazekasgy@37: { fazekasgy@37: if (!m_errorQueue.empty()) { Chris@71: ValueError e = m_errorQueue.front(); fazekasgy@37: m_errorQueue.pop(); fazekasgy@37: if (m_errorQueue.empty()) m_error = false; fazekasgy@37: return e; fazekasgy@37: } fazekasgy@37: else { fazekasgy@37: m_error = false; Chris@71: return ValueError(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /* Utilities */ fazekasgy@37: fazekasgy@37: /// get the type name of an object fazekasgy@37: std::string Chris@71: PyTypeConversions::PyValue_Get_TypeName(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: PyObject *pyType = PyObject_Type(pyValue); fazekasgy@37: if (!pyType) fazekasgy@37: { fazekasgy@37: cerr << "Warning: Object type name could not be found." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: return std::string ("< unknown type >"); fazekasgy@37: } fazekasgy@37: PyObject *pyString = PyObject_Str(pyType); fazekasgy@37: if (!pyString) fazekasgy@37: { fazekasgy@37: cerr << "Warning: Object type name could not be found." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: Py_CLEAR(pyType); fazekasgy@37: return std::string ("< unknown type >"); fazekasgy@37: } fazekasgy@37: char *cstr = PyString_AS_STRING(pyString); fazekasgy@37: if (!cstr) fazekasgy@37: { fazekasgy@37: cerr << "Warning: Object type name could not be found." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: Py_DECREF(pyType); fazekasgy@37: Py_CLEAR(pyString); fazekasgy@37: return std::string("< unknown type >"); fazekasgy@37: } fazekasgy@37: Py_DECREF(pyType); fazekasgy@37: Py_DECREF(pyString); fazekasgy@37: return std::string(cstr); fazekasgy@37: fazekasgy@37: }