Program Listing for File vtkMRMLLayerDMPythonUtil.cxx
↰ Return to documentation for file (MRML/vtkMRMLLayerDMPythonUtil.cxx)
#include "vtkMRMLLayerDMPythonUtil.h"
#include <vtkObjectFactory.h>
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<PyObject*>& 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<int*>(ptr);
return PyLong_FromLong(static_cast<long>(value));
}
case VTK_LONG:
{
long value = *static_cast<long*>(ptr);
return PyLong_FromLong(value);
}
case VTK_FLOAT:
{
float value = *static_cast<float*>(ptr);
return PyFloat_FromDouble(static_cast<double>(value));
}
case VTK_DOUBLE:
{
double value = *static_cast<double*>(ptr);
return PyFloat_FromDouble(value);
}
case VTK_STRING:
{
const char* value = *static_cast<const char**>(ptr);
return PyUnicode_FromString(value);
}
case VTK_OBJECT:
{
if (vtkObject* obj = vtkObject::SafeDownCast(static_cast<vtkObject*>(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());
}