.. _program_listing_file_MRML_vtkMRMLLayerDMPythonUtil.cxx: Program Listing for File vtkMRMLLayerDMPythonUtil.cxx ===================================================== |exhale_lsh| :ref:`Return to documentation for file ` (``MRML/vtkMRMLLayerDMPythonUtil.cxx``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "vtkMRMLLayerDMPythonUtil.h" #include vtkStandardNewMacro(vtkMRMLLayerDMPythonUtil); vtkMRMLLayerDMPythonUtil::vtkMRMLLayerDMPythonUtil() = default; vtkMRMLLayerDMPythonUtil::~vtkMRMLLayerDMPythonUtil() = default; PyObject* vtkMRMLLayerDMPythonUtil::ToPyObject(vtkObjectBase* obj) { return vtkPythonUtil::GetObjectFromPointer(obj); } PyObject* vtkMRMLLayerDMPythonUtil::ToPyObject(unsigned long value) { return PyLong_FromUnsignedLong(value); } PyObject* vtkMRMLLayerDMPythonUtil::ToPyObject(const std::string& value) { return PyUnicode_FromString(value.c_str()); } PyObject* vtkMRMLLayerDMPythonUtil::RawPtrToPython(void* ptr) { if (ptr) { return PyCapsule_New(ptr, nullptr, nullptr); } // Return borrowed reference to Py_None Py_INCREF(Py_None); return Py_None; } vtkSmartPyObject vtkMRMLLayerDMPythonUtil::ToPyArgs(const std::vector& pyObjs) { vtkPythonScopeGilEnsurer gilEnsurer; if (pyObjs.empty()) { return {}; } // Pack into a Python tuple and transfer ownership PyObject* pyTuple = PyTuple_New(pyObjs.size()); for (size_t i = 0; i < (pyObjs.size()); ++i) { PyTuple_SET_ITEM(pyTuple, i, pyObjs[i]); } return { pyTuple }; } vtkSmartPyObject vtkMRMLLayerDMPythonUtil::ToPyArgs(vtkObjectBase* obj) { vtkPythonScopeGilEnsurer gilEnsurer; return ToPyArgs({ ToPyObject(obj) }); } vtkSmartPyObject vtkMRMLLayerDMPythonUtil::ToPyArgs(vtkObject* obj, unsigned long eventId, void* callData) { vtkPythonScopeGilEnsurer gilEnsurer; return ToPyArgs({ ToPyObject(obj), ToPyObject(eventId), RawPtrToPython(callData) }); } PyObject* vtkMRMLLayerDMPythonUtil::CastCallData(PyObject* object, int vtkType) { vtkPythonScopeGilEnsurer gilEnsurer; if (!IsValidPythonContext()) { return nullptr; } if (!PyCapsule_CheckExact(object)) { PyErr_SetString(PyExc_TypeError, "Expected a PyCapsule object"); return nullptr; } void* ptr = PyCapsule_GetPointer(object, nullptr); if (!ptr) { PyErr_SetString(PyExc_NotImplementedError, "Invalid call data object"); return nullptr; } switch (vtkType) { case VTK_INT: { int value = *static_cast(ptr); return PyLong_FromLong(static_cast(value)); } case VTK_LONG: { long value = *static_cast(ptr); return PyLong_FromLong(value); } case VTK_FLOAT: { float value = *static_cast(ptr); return PyFloat_FromDouble(static_cast(value)); } case VTK_DOUBLE: { double value = *static_cast(ptr); return PyFloat_FromDouble(value); } case VTK_STRING: { const char* value = *static_cast(ptr); return PyUnicode_FromString(value); } case VTK_OBJECT: { if (vtkObject* obj = vtkObject::SafeDownCast(static_cast(ptr))) { return ToPyObject(obj); } PyErr_SetString(PyExc_NotImplementedError, "Invalid VTK object"); return nullptr; } default: { PyErr_SetString(PyExc_ValueError, "Unknown vtkType. Expected one of : [VTK_INT, VTK_LONG, VTK_FLOAT, VTK_DOUBLE, VTK_STRING, VTK_OBJECT]"); return nullptr; } } } PyObject* vtkMRMLLayerDMPythonUtil::CallPythonMethod(PyObject* object, const vtkSmartPyObject& pyArgs, const std::string& fName) { if (!IsValidPythonContext() || !object) { return nullptr; } vtkPythonScopeGilEnsurer gilEnsurer; PyObject* method = PyObject_GetAttrString(object, fName.c_str()); if (!method) { // Don't modify Python's error reporting return nullptr; } if (!PyCallable_Check(method)) { // PyCallable_Check doesn't raise any errors. Raise called attribute isn't callable. const auto errorString = std::string("vtkMRMLLayerDMPythonUtil::") + __func__ + ": Attribute is not callable : '" + fName + "' of object : " + GetObjectStr(object); PyErr_SetString(PyExc_TypeError, errorString.c_str()); return nullptr; } return CallPythonObject(method, pyArgs); } PyObject* vtkMRMLLayerDMPythonUtil::CallPythonObject(PyObject* object, const vtkSmartPyObject& pyArgs) { if (!IsValidPythonContext() || !object) { return nullptr; } vtkPythonScopeGilEnsurer gilEnsurer; if (!PyCallable_Check(object)) { // PyCallable_Check doesn't raise any errors. Raise called attribute isn't callable. const auto errorString = std::string("vtkMRMLLayerDMPythonUtil::") + __func__ + ": Object is not callable : " + GetObjectStr(object); PyErr_SetString(PyExc_TypeError, errorString.c_str()); return nullptr; } return PyObject_CallObject(object, pyArgs); } void vtkMRMLLayerDMPythonUtil::SetPythonObject(PyObject** destObject, PyObject* object) { if (!IsValidPythonContext()) { return; } if (object == (*destObject)) { return; } vtkPythonScopeGilEnsurer gilEnsurer; DeletePythonObject(destObject); *destObject = object; Py_XINCREF(*destObject); } void vtkMRMLLayerDMPythonUtil::DeletePythonObject(PyObject** destObject) { if (!Py_IsInitialized()) { return; } vtkPythonScopeGilEnsurer gilEnsurer; Py_XDECREF(*destObject); *destObject = nullptr; } std::string vtkMRMLLayerDMPythonUtil::GetObjectStr(PyObject* object) { if (!Py_IsInitialized()) { return {}; } if (!object) { return "None"; } // Save current errors to avoid changing the current python error stack if any PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); std::string objectString{ "INVALID_OBJECT_STR" }; if (auto strObj = PyObject_Str(object)) { objectString = PyUnicode_AsUTF8(strObj); Py_DECREF(strObj); } // Restore the python error stack PyErr_Restore(type, value, traceback); return objectString; } bool vtkMRMLLayerDMPythonUtil::IsValidPythonContext() { if (!Py_IsInitialized()) { return false; } vtkPythonScopeGilEnsurer gilEnsurer; return !PyErr_Occurred(); } std::string vtkMRMLLayerDMPythonUtil::FormatExceptionTraceback() { // Don't use IsValidPythonContext here as it checks if no error has occurred if (!Py_IsInitialized()) { return {}; } vtkPythonScopeGilEnsurer gilEnsurer; if (!PyErr_Occurred()) { // No error has occurred return {}; } // Get error string from the traceback module PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyErr_NormalizeException(&type, &value, &traceback); PyObject* tracebackModule = PyImport_ImportModule("traceback"); PyObject* formatExceptionFunc = PyObject_GetAttrString(tracebackModule, "format_exception"); PyObject* args = PyTuple_Pack(3, type, value, traceback); PyObject* formattedList = PyObject_CallObject(formatExceptionFunc, args); PyObject* emptyString = PyUnicode_FromString(""); PyObject* formatted = PyUnicode_Join(emptyString, formattedList); std::string exceptionTraceback = PyUnicode_AsUTF8(formatted); // Cleanup PyErr_Restore(type, value, traceback); Py_XDECREF(formatted); Py_XDECREF(emptyString); Py_XDECREF(formattedList); Py_XDECREF(args); Py_XDECREF(formatExceptionFunc); Py_XDECREF(tracebackModule); return exceptionTraceback; } void vtkMRMLLayerDMPythonUtil::PrintErrorTraceback(const vtkObject* object, const std::string& errorMsg) { // If the traceback is not empty, print the traceback using vtkErrorMacro const auto traceback = FormatExceptionTraceback(); if (traceback.empty()) { return; } std::string errorString{ errorMsg }; if (!errorString.empty()) { errorString += "\n"; } errorString += traceback; vtkErrorWithObjectMacro(object, "" << traceback.c_str()); }