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