// Copyright (c) 2013-2020 Josh Blum // 2019-2021 Nicholas Corgan // SPDX-License-Identifier: BSL-1.0 #include #include #include #include #include #include #include "PythonProxy.hpp" /*********************************************************************** * SFINAE typedefs **********************************************************************/ template using EnableIfUnsigned = typename std::enable_if::value && (sizeof(T) < 8), U>::type; template using EnableIfSigned = typename std::enable_if::value && (sizeof(T) < 8), U>::type; template using EnableIfUnsigned64 = typename std::enable_if::value && (sizeof(T) == 8), U>::type; template using EnableIfSigned64 = typename std::enable_if::value && (sizeof(T) == 8), U>::type; /*********************************************************************** * Object -- Allow the use of native python objects. * * When Pothos::Object is used in the C++ API as a return or field, * it will arrive to the converter as an Object containing an Object. * This converter handles un-nesting the object into a native PyObject. * If the conversion fails, then convertObjectToProxy() simply wraps * the object into a managed Proxy that can be handled by "ProxyType". **********************************************************************/ static Pothos::Proxy convertObjectToPyObject(Pothos::ProxyEnvironment::Sptr env, const Pothos::Object &obj) { return env->convertObjectToProxy(obj); } pothos_static_block(pothosRegisterPythonObjectConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/object_to_pyobject", &convertObjectToPyObject); } /*********************************************************************** * None **********************************************************************/ static Pothos::Proxy convertNullToPyNone(Pothos::ProxyEnvironment::Sptr env, const Pothos::NullObject &) { return std::dynamic_pointer_cast(env)->makeHandle(Py_None, REF_BORROWED); } static Pothos::NullObject convertPyNoneToNull(const Pothos::Proxy &) { return Pothos::NullObject(); } pothos_static_block(pothosRegisterPythonNullConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/null_to_pynone", &convertNullToPyNone); Pothos::PluginRegistry::add("/proxy/converters/python/pynone_to_null", Pothos::ProxyConvertPair("NoneType", &convertPyNoneToNull)); } /*********************************************************************** * bool **********************************************************************/ static Pothos::Proxy convertBoolToPyBool(Pothos::ProxyEnvironment::Sptr env, const bool &b) { return std::dynamic_pointer_cast(env)->makeHandle(PyBool_FromLong(b), REF_NEW); } static bool convertPyBoolToBool(const Pothos::Proxy &proxy) { return bool(std::dynamic_pointer_cast(proxy.getHandle())->obj == Py_True); } pothos_static_block(pothosRegisterPythonBoolConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/bool_to_pybool", &convertBoolToPyBool); Pothos::PluginRegistry::add("/proxy/converters/python/pybool_to_bool", Pothos::ProxyConvertPair("bool", &convertPyBoolToBool)); } /*********************************************************************** * integer/long types **********************************************************************/ template static EnableIfSigned convertIntNumToPyNum(Pothos::ProxyEnvironment::Sptr env, const T &num) { #if PY_MAJOR_VERSION >= 3 return std::dynamic_pointer_cast(env)->makeHandle(PyLong_FromLong(long(num)), REF_NEW); #else return std::dynamic_pointer_cast(env)->makeHandle(PyInt_FromLong(long(num)), REF_NEW); #endif } template static EnableIfUnsigned convertIntNumToPyNum(Pothos::ProxyEnvironment::Sptr env, const T &num) { #if PY_MAJOR_VERSION >= 3 return std::dynamic_pointer_cast(env)->makeHandle(PyLong_FromUnsignedLong((unsigned long)(num)), REF_NEW); #else return std::dynamic_pointer_cast(env)->makeHandle(PyInt_FromSize_t((size_t)(num)), REF_NEW); #endif } template static EnableIfSigned64 convertIntNumToPyNum(Pothos::ProxyEnvironment::Sptr env, const T &num) { return std::dynamic_pointer_cast(env)->makeHandle(PyLong_FromLongLong((long long)num), REF_NEW); } template static EnableIfUnsigned64 convertIntNumToPyNum(Pothos::ProxyEnvironment::Sptr env, const T &num) { return std::dynamic_pointer_cast(env)->makeHandle(PyLong_FromUnsignedLongLong((unsigned long long)num), REF_NEW); } static Pothos::Object convertPyLongToLongLong(const Pothos::Proxy &proxy) { //Try to convert to a signed long long, //however return an unsigned long long in the case of overflow. //The result is returned as a Pothos::Object to erase the type //and convertProxyToObject handles this special case. int overflow(0); auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; auto r = PyLong_AsLongLongAndOverflow(obj, &overflow); if (overflow) return Pothos::Object(PyLong_AsUnsignedLongLongMask(obj)); return Pothos::Object(r); } pothos_static_block(pothosRegisterPythonIntConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/char_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/schar_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/uchar_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/sshort_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/ushort_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/sint_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/uint_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/slong_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/ulong_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/sllong_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::addCall("/proxy/converters/python/ullong_to_pynum", &convertIntNumToPyNum); Pothos::PluginRegistry::add("/proxy/converters/python/pyint_to_llong", Pothos::ProxyConvertPair("int", &convertPyLongToLongLong)); //int and long are distinct types in python2, so long gets its own entry #if PY_MAJOR_VERSION < 3 Pothos::PluginRegistry::add("/proxy/converters/python/pylong_to_llong", Pothos::ProxyConvertPair("long", &convertPyLongToLongLong)); #endif } /*********************************************************************** * float types **********************************************************************/ template static Pothos::Proxy convertFloatNumToPyFloat(Pothos::ProxyEnvironment::Sptr env, const T &num) { return std::dynamic_pointer_cast(env)->makeHandle(PyFloat_FromDouble(double(num)), REF_NEW); } static double convertPyFloatToDouble(const Pothos::Proxy &proxy) { return PyFloat_AsDouble(std::dynamic_pointer_cast(proxy.getHandle())->obj); } pothos_static_block(pothosRegisterPythonFloatConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/float_to_pyfloat", &convertFloatNumToPyFloat); Pothos::PluginRegistry::addCall("/proxy/converters/python/double_to_pyfloat", &convertFloatNumToPyFloat); Pothos::PluginRegistry::add("/proxy/converters/python/pyfloat_to_double", Pothos::ProxyConvertPair("float", &convertPyFloatToDouble)); } /*********************************************************************** * complex types **********************************************************************/ template static Pothos::Proxy convertComplexToPyComplex(Pothos::ProxyEnvironment::Sptr env, const std::complex &c) { return std::dynamic_pointer_cast(env)->makeHandle(PyComplex_FromDoubles(double(c.real()), double(c.imag())), REF_NEW); } static std::complex convertPyComplexToComplex(const Pothos::Proxy &proxy) { auto c = PyComplex_AsCComplex(std::dynamic_pointer_cast(proxy.getHandle())->obj); return std::complex(c.real, c.imag); } pothos_static_block(pothosRegisterPythonComplexConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/complex64_to_pycomplex", &convertComplexToPyComplex); Pothos::PluginRegistry::addCall("/proxy/converters/python/complex128_to_pycomplex", &convertComplexToPyComplex); Pothos::PluginRegistry::add("/proxy/converters/python/pycomplex_to_complex128", Pothos::ProxyConvertPair("complex", &convertPyComplexToComplex)); } /*********************************************************************** * string **********************************************************************/ static Pothos::Proxy convertStringToPyString(Pothos::ProxyEnvironment::Sptr env, const std::string &s) { return std::dynamic_pointer_cast(env)->makeHandle(StdStringToPyObject(s), REF_NEW); } static std::string convertPyStringToString(const Pothos::Proxy &proxy) { auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; return PyObjToStdString(obj); } static Pothos::Proxy convertStrVecToPyList(Pothos::ProxyEnvironment::Sptr env, const std::vector &strings) { Pothos::ProxyVector vec; for (const auto &s : strings) vec.push_back(env->makeProxy(s)); return env->makeProxy(vec); } pothos_static_block(pothosRegisterPythonStringConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/string_to_pystring", &convertStringToPyString); Pothos::PluginRegistry::add("/proxy/converters/python/pystring_to_string", Pothos::ProxyConvertPair("str", &convertPyStringToString)); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecstring_to_pylist", &convertStrVecToPyList); } /*********************************************************************** * bytes **********************************************************************/ template Pothos::Proxy convertByteVectorToPyBytes(Pothos::ProxyEnvironment::Sptr env, const std::vector &vec) { auto o = PyBytes_FromStringAndSize(reinterpret_cast(vec.data()), vec.size()); return std::dynamic_pointer_cast(env)->makeHandle(o, REF_NEW); } static std::vector convertPyBytesToString(const Pothos::Proxy &proxy) { auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; auto c = PyBytes_AsString(obj); return std::vector(c, c+PyBytes_Size(obj)); } static std::vector convertPyByteArrayToString(const Pothos::Proxy &proxy) { auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; auto c = PyByteArray_AsString(obj); return std::vector(c, c+PyByteArray_Size(obj)); } pothos_static_block(pothosRegisterPythonBytesConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/vecchar_to_pybytes", &convertByteVectorToPyBytes); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecschar_to_pybytes", &convertByteVectorToPyBytes); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecuchar_to_pybytes", &convertByteVectorToPyBytes); Pothos::PluginRegistry::add("/proxy/converters/python/pybytes_to_string", Pothos::ProxyConvertPair("bytes", &convertPyBytesToString)); Pothos::PluginRegistry::add("/proxy/converters/python/pybytearray_to_string", Pothos::ProxyConvertPair("bytearray", &convertPyByteArrayToString)); } /*********************************************************************** * numeric **********************************************************************/ template static Pothos::Proxy convertIntVectorToPyList(Pothos::ProxyEnvironment::Sptr env, const std::vector &vec) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyList(PyList_New(vec.size()), REF_NEW); for (size_t i = 0; i < vec.size(); i++) { #if PY_MAJOR_VERSION >= 3 PyList_SetItem(pyList.obj, i, PyLong_FromLong(long(vec[i]))); #else PyList_SetItem(pyList.obj, i, PyInt_FromLong(long(vec[i]))); #endif } return pyenv->makeHandle(pyList); } template static Pothos::Proxy convertLongLongVectorToPyList(Pothos::ProxyEnvironment::Sptr env, const std::vector &vec) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyList(PyList_New(vec.size()), REF_NEW); for (size_t i = 0; i < vec.size(); i++) { PyList_SetItem(pyList.obj, i, PyLong_FromLongLong((long long)(vec[i]))); } return pyenv->makeHandle(pyList); } template static Pothos::Proxy convertFloatVectorToPyList(Pothos::ProxyEnvironment::Sptr env, const std::vector &vec) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyList(PyList_New(vec.size()), REF_NEW); for (size_t i = 0; i < vec.size(); i++) { PyList_SetItem(pyList.obj, i, PyFloat_FromDouble(double(vec[i]))); } return pyenv->makeHandle(pyList); } template static Pothos::Proxy convertComplexVectorToPyList(Pothos::ProxyEnvironment::Sptr env, const std::vector> &vec) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyList(PyList_New(vec.size()), REF_NEW); for (size_t i = 0; i < vec.size(); i++) { PyList_SetItem(pyList.obj, i, PyComplex_FromDoubles(double(vec[i].real()), double(vec[i].imag()))); } return pyenv->makeHandle(pyList); } pothos_static_block(pothosRegisterPythonNumericConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/vecsshort_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecushort_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecsint_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecuint_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecslong_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/veculong_to_pylist", &convertIntVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecsllong_to_pylist", &convertLongLongVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecullong_to_pylist", &convertLongLongVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecfloat_to_pylist", &convertFloatVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/vecdouble_to_pylist", &convertFloatVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/veccomplexfloat_to_pylist", &convertComplexVectorToPyList); Pothos::PluginRegistry::addCall("/proxy/converters/python/veccomplexdouble_to_pylist", &convertComplexVectorToPyList); } /*********************************************************************** * tuple **********************************************************************/ static Pothos::ProxyVector convertPyTupleToVector(const Pothos::Proxy &proxy) { auto env = std::dynamic_pointer_cast(proxy.getEnvironment()); auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; Pothos::ProxyVector vec(PyTuple_Size(obj)); for (size_t i = 0; i < vec.size(); i++) { vec[i] = env->makeHandle(PyTuple_GetItem(obj, i), REF_BORROWED); } return vec; } pothos_static_block(pothosRegisterPythonTupleConversions) { Pothos::PluginRegistry::add("/proxy/converters/python/pytuple_to_vector", Pothos::ProxyConvertPair("tuple", &convertPyTupleToVector)); } /*********************************************************************** * list **********************************************************************/ static Pothos::Proxy convertVectorToPyList(Pothos::ProxyEnvironment::Sptr env, const Pothos::ProxyVector &vec) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyList(PyList_New(vec.size()), REF_NEW); for (size_t i = 0; i < vec.size(); i++) { PyList_SetItem(pyList.obj, i, pyenv->getHandle(vec[i])->ref.newRef()); } return pyenv->makeHandle(pyList); } static Pothos::ProxyVector convertPyListToVector(const Pothos::Proxy &proxy) { auto env = std::dynamic_pointer_cast(proxy.getEnvironment()); auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; Pothos::ProxyVector vec(PyList_Size(obj)); for (size_t i = 0; i < vec.size(); i++) { vec[i] = env->makeHandle(PyList_GetItem(obj, i), REF_BORROWED); } return vec; } pothos_static_block(pothosRegisterPythonListConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/vector_to_pylist", &convertVectorToPyList); Pothos::PluginRegistry::add("/proxy/converters/python/pylist_to_vector", Pothos::ProxyConvertPair("list", &convertPyListToVector)); } /*********************************************************************** * set **********************************************************************/ static Pothos::Proxy convertSetToPySet(Pothos::ProxyEnvironment::Sptr env, const Pothos::ProxySet &set) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pySet(PySet_New(nullptr), REF_NEW); for (const auto &entry : set) { auto obj = pyenv->getHandle(entry)->obj; PySet_Add(pySet.obj, obj); } return pyenv->makeHandle(pySet); } static Pothos::ProxySet convertPySetToSet(const Pothos::Proxy &proxy) { auto env = std::dynamic_pointer_cast(proxy.getEnvironment()); auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; auto pySet = PySet_New(obj); //makes a copy which we pop from until empty Pothos::ProxySet set; while (PySet_Size(pySet) != 0) { set.insert(env->makeHandle(PySet_Pop(pySet), REF_NEW)); } return set; } pothos_static_block(pothosRegisterPythonSetConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/set_to_pyset", &convertSetToPySet); Pothos::PluginRegistry::add("/proxy/converters/python/pyset_to_set", Pothos::ProxyConvertPair("set", &convertPySetToSet)); Pothos::PluginRegistry::add("/proxy/converters/python/pyfrozenset_to_set", Pothos::ProxyConvertPair("frozenset", &convertPySetToSet)); } /*********************************************************************** * dict **********************************************************************/ static Pothos::Proxy convertMapToPyDict(Pothos::ProxyEnvironment::Sptr env, const Pothos::ProxyMap &d) { auto pyenv = std::dynamic_pointer_cast(env); PyObjectRef pyDict(PyDict_New(), REF_NEW); for (const auto &entry : d) PyDict_SetItem( pyDict.obj, pyenv->getHandle(entry.first.getHandle())->obj, pyenv->getHandle(entry.second.getHandle())->obj); return pyenv->makeHandle(pyDict); } static Pothos::ProxyMap convertPyDictToMap(const Pothos::Proxy &proxy) { auto env = std::dynamic_pointer_cast(proxy.getEnvironment()); auto obj = std::dynamic_pointer_cast(proxy.getHandle())->obj; Pothos::ProxyMap d; PyObjectRef items(PyDict_Items(obj), REF_NEW); for (Py_ssize_t i = 0; i < PyList_Size(items.obj); i++) { PyObject *item = PyList_GetItem(items.obj, i); assert(PyTuple_Check(item) and PyTuple_Size(item) == 2); auto key = env->makeHandle(PyTuple_GetItem(item, 0), REF_BORROWED); auto val = env->makeHandle(PyTuple_GetItem(item, 1), REF_BORROWED); d[key] = val; } return d; } pothos_static_block(pothosRegisterPythonDictConversions) { Pothos::PluginRegistry::addCall("/proxy/converters/python/map_to_pydict", &convertMapToPyDict); Pothos::PluginRegistry::add("/proxy/converters/python/pydict_to_map", Pothos::ProxyConvertPair("dict", &convertPyDictToMap)); }