diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 264d812..127835e 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -387,7 +387,7 @@ QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QS throw MissingImplementation(this->className, "genFilePreview"); boost::python::object pyVersion = implementation(fileName, maxSize); // We need responsibility for deleting the QWidget to be transferred to C++ - sipAPI()->api_transfer_to(pyVersion.ptr(), Py_None); + sipAPIAccess::sipAPI()->api_transfer_to(pyVersion.ptr(), Py_None); return boost::python::extract(pyVersion)(); } PYCATCH; } diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index c7497b0..36b45a6 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -530,14 +530,14 @@ PyObject *toPyQt(T *objPtr) qDebug("no input object"); return bpy::incref(Py_None); } - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + const sipTypeDef *type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { qDebug("failed to determine type: %s", MetaData::className()); return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type(objPtr, type, 0); + PyObject *sipObj = sipAPIAccess::sipAPI()->api_convert_from_type(objPtr, type, 0); if (sipObj == nullptr) { qDebug("failed to convert"); return bpy::incref(Py_None); @@ -564,19 +564,19 @@ struct QClass_converters } static PyObject *convert(const T &object) { - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + const sipTypeDef *type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); + PyObject *sipObj = sipAPIAccess::sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } if (std::is_copy_constructible_v) // Ensure Python deletes the C++ component - sipAPI()->api_transfer_back(sipObj); + sipAPIAccess::sipAPI()->api_transfer_back(sipObj); return bpy::incref(sipObj); } @@ -586,19 +586,19 @@ struct QClass_converters return bpy::incref(Py_None); } - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + const sipTypeDef *type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); + PyObject *sipObj = sipAPIAccess::sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } if (std::is_copy_constructible_v) // Ensure Python deletes the C++ component - sipAPI()->api_transfer_back(sipObj); + sipAPIAccess::sipAPI()->api_transfer_back(sipObj); return bpy::incref(sipObj); } @@ -613,11 +613,11 @@ struct QClass_converters // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that // Instead, this should be called within the wrappers for functions which return deletable pointers. //sipAPI()->api_transfer_to(objPtr, Py_None); - if (PyObject_TypeCheck(objPtr, sipAPI()->api_simplewrapper_type)) { + if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_simplewrapper_type)) { sipSimpleWrapper *wrapper; wrapper = reinterpret_cast(objPtr); return wrapper->data; - } else if (PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + } else if (PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_wrapper_type)) { sipWrapper *wrapper; wrapper = reinterpret_cast(objPtr); return wrapper->super.data; @@ -651,12 +651,12 @@ struct QInterface_converters struct QInterface_to_PyQt { static PyObject *convert(const T &object) { - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + const sipTypeDef *type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + PyObject *sipObj = sipAPIAccess::sipAPI()->api_convert_from_type((void*)(&object), type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } @@ -669,12 +669,12 @@ struct QInterface_converters return bpy::incref(Py_None); } - const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); + const sipTypeDef *type = sipAPIAccess::sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + PyObject *sipObj = sipAPIAccess::sipAPI()->api_convert_from_type(object, type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } @@ -689,7 +689,7 @@ struct QInterface_converters static void *QInterface_from_PyQt(PyObject *objPtr) { - if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + if (!PyObject_TypeCheck(objPtr, sipAPIAccess::sipAPI()->api_wrapper_type)) { bpy::throw_error_already_set(); } diff --git a/src/runner/sipapiaccess.cpp b/src/runner/sipapiaccess.cpp new file mode 100644 index 0000000..e88afb6 --- /dev/null +++ b/src/runner/sipapiaccess.cpp @@ -0,0 +1,84 @@ +#include "sipapiaccess.h" +#include +#include +#include + +const sipAPIDef* sipAPIAccess::sipAPI() +{ + QString exception; + static const sipAPIDef* sipApi = nullptr; + if (sipApi == nullptr) { + #if defined(SIP_USE_PYCAPSULE) + PyImport_ImportModule("PyQt5.sip"); + + auto errorObj = PyErr_Occurred(); + if (errorObj != NULL) { + PyObject* type, * value, * traceback; + PyErr_Fetch(&type, &value, &traceback); + PyErr_NormalizeException(&type, &value, &traceback); + if (traceback != NULL) { + boost::python::handle<> h_type(type); + boost::python::handle<> h_val(value); + boost::python::handle<> h_tb(traceback); + boost::python::object tb(boost::python::import("traceback")); + boost::python::object fmt_exp(tb.attr("format_exception")); + boost::python::object exp_list(fmt_exp(h_type, h_val, h_tb)); + boost::python::object exp_str(boost::python::str("\n").join(exp_list)); + boost::python::extract returned(exp_str); + exception = QString::fromStdString(returned()); + } + PyErr_Restore(type, value, traceback); + throw MOBase::MyException(QString("Failed to load PyQt5: %1").arg(exception)); + } + + sipApi = (const sipAPIDef*)PyCapsule_Import("PyQt5.sip._C_API", 0); + if (sipApi == NULL) { + auto errorObj = PyErr_Occurred(); + if (errorObj != NULL) { + PyObject* type, * value, * traceback; + PyErr_Fetch(&type, &value, &traceback); + PyErr_NormalizeException(&type, &value, &traceback); + if (traceback != NULL) { + boost::python::handle<> h_tb(traceback); + boost::python::object tb(boost::python::import("traceback")); + boost::python::object fmt_tb(tb.attr("format_tb")); + boost::python::object tb_list(fmt_tb(h_tb)); + boost::python::object tb_str(boost::python::str("\n").join(tb_list)); + boost::python::extract returned(tb_str); + exception = QString::fromStdString(returned()); + } + PyErr_Restore(type, value, traceback); + } + throw MOBase::MyException(QString("Failed to load SIP API: %1").arg(exception)); + } + #else + PyObject* sip_module; + PyObject* sip_module_dict; + PyObject* c_api; + + /* Import the SIP module. */ + sip_module = PyImport_ImportModule("PyQt5.sip"); + + if (sip_module == NULL) + return NULL; + + /* Get the module's dictionary. */ + sip_module_dict = PyModule_GetDict(sip_module); + + /* Get the "_C_API" attribute. */ + c_api = PyDict_GetItemString(sip_module_dict, "_C_API"); + + if (c_api == NULL) + return NULL; + + /* Sanity check that it is the right type. */ + if (!PyCObject_Check(c_api)) + return NULL; + + /* Get the actual pointer from the object. */ + sipApi = (const sipAPIDef*)PyCObject_AsVoidPtr(c_api); + #endif + } + + return sipApi; +} \ No newline at end of file diff --git a/src/runner/sipapiaccess.h b/src/runner/sipapiaccess.h index acbdfbc..2706dda 100644 --- a/src/runner/sipapiaccess.h +++ b/src/runner/sipapiaccess.h @@ -3,43 +3,10 @@ #include -static const sipAPIDef *sipAPI() +class sipAPIAccess { - static const sipAPIDef *sipApi = nullptr; - if (sipApi == nullptr) { -#if defined(SIP_USE_PYCAPSULE) - PyImport_ImportModule("PyQt5.sip"); - sipApi = (const sipAPIDef *)PyCapsule_Import("PyQt5.sip._C_API", 0); -#else - PyObject *sip_module; - PyObject *sip_module_dict; - PyObject *c_api; - - /* Import the SIP module. */ - sip_module = PyImport_ImportModule("PyQt5.sip"); - - if (sip_module == NULL) - return NULL; - - /* Get the module's dictionary. */ - sip_module_dict = PyModule_GetDict(sip_module); - - /* Get the "_C_API" attribute. */ - c_api = PyDict_GetItemString(sip_module_dict, "_C_API"); - - if (c_api == NULL) - return NULL; - - /* Sanity check that it is the right type. */ - if (!PyCObject_Check(c_api)) - return NULL; - - /* Get the actual pointer from the object. */ - sipApi = (const sipAPIDef *)PyCObject_AsVoidPtr(c_api); -#endif - } - - return sipApi; -} +public: + static const sipAPIDef* sipAPI(); +}; #endif // SIPAPIACCESS_H