/*========================================================================= Program: ParaView Module: vtkClientServerInterpreterPython.cxx Copyright (c) Kitware, Inc. All rights reserved. See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkPython.h" #include "vtkClientServerInterpreter.h" #include "vtkClientServerStream.h" #include "vtkPythonInterpreter.h" #include "vtkPythonUtil.h" #include "vtkSmartPyObject.h" namespace { struct PythonCSShimData { PythonCSShimData(PyObject* obj) { this->Object = obj; Py_INCREF(this->Object); } ~PythonCSShimData() { Py_DECREF(this->Object); } PyObject* Object; }; } //---------------------------------------------------------------------------- static void FreePythonCSShimData(void* ctx) { PythonCSShimData* data = (PythonCSShimData*)ctx; delete data; } #define Py_RETURN_CHECKED(pyobj) \ do \ { \ if (pyobj) \ { \ return pyobj; \ } \ else \ { \ Py_RETURN_NONE; \ } \ } while (false) //---------------------------------------------------------------------------- template static PyObject* PythonConvert(T const& value) { long longValue = value; Py_RETURN_CHECKED(PyLong_FromLong(longValue)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(int const& value) { long longValue = value; Py_RETURN_CHECKED(PyInt_FromLong(longValue)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(unsigned int const& value) { unsigned long unsignedLongValue = value; Py_RETURN_CHECKED(PyLong_FromUnsignedLong(unsignedLongValue)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(unsigned long const& value) { Py_RETURN_CHECKED(PyLong_FromUnsignedLong(value)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(long long const& value) { Py_RETURN_CHECKED(PyLong_FromLongLong(value)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(unsigned long long const& value) { Py_RETURN_CHECKED(PyLong_FromUnsignedLongLong(value)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(double const& value) { Py_RETURN_CHECKED(PyFloat_FromDouble(value)); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(float const& value) { return PythonConvert(value); } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(bool const& value) { if (value) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } //---------------------------------------------------------------------------- template <> PyObject* PythonConvert(vtkStdString const& value) { Py_RETURN_CHECKED(PyString_FromStringAndSize(value.c_str(), value.size())); } //---------------------------------------------------------------------------- template static PyObject* MakeArrayOf(const vtkClientServerStream& msg, int arg, vtkTypeUInt32 length) { T* argArray = new T[length]; if (!msg.GetArgument(0, arg, argArray, length)) { delete [] argArray; argArray = NULL; Py_RETURN_NONE; } PyObject* tuple = PyTuple_New(length); for (vtkTypeUInt32 i = 0; i < length; ++i) { PyTuple_SET_ITEM(tuple, i, PythonConvert(argArray[i])); } delete [] argArray; return tuple; } //---------------------------------------------------------------------------- template static PyObject* MakeScalar(const vtkClientServerStream& msg, int arg) { T argValue; if (!msg.GetArgument(0, arg, &argValue)) { Py_RETURN_NONE; } return PythonConvert(argValue); } //---------------------------------------------------------------------------- static vtkObjectBase* NewInstanceCallback(void* ctx) { PythonCSShimData* data = (PythonCSShimData*)ctx; vtkSmartPyObject args(PyTuple_New(0)); vtkSmartPyObject instance(PyObject_Call(data->Object, args, NULL)); vtkObjectBase* obj = PyVTKObject_GetObject(instance); obj->Register(NULL); return obj; } //---------------------------------------------------------------------------- static void ReturnResult(PyObject* result, vtkClientServerStream& resultStream) { resultStream.Reset(); if (PyVTKObject_Check(result)) { vtkObjectBase* obj = PyVTKObject_GetObject(result); resultStream << vtkClientServerStream::Reply << obj << vtkClientServerStream::End; } else if (PyString_Check(result)) { char* str = PyString_AsString(result); resultStream << vtkClientServerStream::Reply << str << vtkClientServerStream::End; } else if (PyBool_Check(result)) { bool num = result == Py_True; resultStream << vtkClientServerStream::Reply << num << vtkClientServerStream::End; } else if (PyLong_Check(result)) { #if PY_VERSION_HEX >= 0x02070000 int overflow; PY_LONG_LONG num = PyLong_AsLongLongAndOverflow(result, &overflow); if (!overflow) { resultStream << vtkClientServerStream::Reply << num << vtkClientServerStream::End; } else { unsigned PY_LONG_LONG unum = PyLong_AsUnsignedLongLong(result); resultStream << vtkClientServerStream::Reply << unum << vtkClientServerStream::End; } #else PY_LONG_LONG num = PyLong_AsLongLong(result); if (PyErr_Occurred()) { PyErr_Clear(); unsigned PY_LONG_LONG unum = PyLong_AsUnsignedLongLong(result); resultStream << vtkClientServerStream::Reply << unum << vtkClientServerStream::End; } else { resultStream << vtkClientServerStream::Reply << num << vtkClientServerStream::End; } #endif } else if (PyFloat_Check(result)) { double num = PyFloat_AsDouble(result); resultStream << vtkClientServerStream::Reply << num << vtkClientServerStream::End; } else if (PyInt_Check(result)) { long num = PyInt_AsLong(result); resultStream << vtkClientServerStream::Reply << num << vtkClientServerStream::End; } else if (PyTuple_Check(result)) { Py_ssize_t size = PyTuple_Size(result); // Check the type of the first item to see what we should do. PyObject* sentinel_item = PyTuple_GET_ITEM(result, 0); #define convert_array(convert, type) \ do \ { \ type* arr = new type[size]; \ for (Py_ssize_t i = 0; i < size; ++i) \ { \ PyObject* item = PyTuple_GET_ITEM(result, i); \ type num = convert(item); \ arr[i] = num; \ } \ resultStream \ << vtkClientServerStream::Reply \ << vtkClientServerStream::InsertArray(arr, size) \ << vtkClientServerStream::End; \ delete [] arr; \ } while (false) if (PyLong_Check(sentinel_item)) { convert_array(PyLong_AsUnsignedLongLong, unsigned PY_LONG_LONG); } else if (PyInt_Check(sentinel_item)) { convert_array(PyInt_AsLong, long); } else if (PyFloat_Check(sentinel_item)) { convert_array(PyFloat_AsDouble, double); } #undef convert_array } else { resultStream << vtkClientServerStream::Error << "Unable to convert result from Python" << vtkClientServerStream::End; } } //---------------------------------------------------------------------------- static vtkStdString GetPythonErrorString() { PyObject* type; PyObject* value; PyObject* traceback; // Increments refcounts for returns. PyErr_Fetch(&type, &value, &traceback); // place the results in vtkSmartPyObjects so the reference counts // are automatically decremented vtkSmartPyObject sType(type); vtkSmartPyObject sValue(value); vtkSmartPyObject sTraceback(traceback); if (!sType) { return "No error from Python?!"; } vtkSmartPyObject pyexc_string(PyObject_Str(sValue)); vtkStdString exc_string; if (pyexc_string) { exc_string = PyString_AsString(pyexc_string); } else { exc_string = ""; } PyErr_Clear(); return exc_string; } //---------------------------------------------------------------------------- static int CommandFunctionCallback(vtkClientServerInterpreter* /*interp*/, vtkObjectBase* ptr, const char* method, const vtkClientServerStream& msg, vtkClientServerStream& result, void* ctx) { PythonCSShimData* data = (PythonCSShimData*)ctx; if (!PyObject_HasAttrString(data->Object, method)) { if (result.GetNumberOfMessages() > 0 && result.GetCommand(0) == vtkClientServerStream::Error && result.GetNumberOfArguments(0) > 1) { /* A superclass wrapper prepared a special message. */ return 0; } vtkOStrStreamWrapper vtkmsg; vtkmsg << "Object type: " << ptr->GetClassName() << ", could not find requested method: \"" << method << "\".\n"; result.Reset(); result << vtkClientServerStream::Error << vtkmsg.str() << vtkClientServerStream::End; vtkmsg.rdbuf()->freeze(0); return 0; } vtkSmartPyObject vtkObject(vtkPythonUtil::GetObjectFromPointer(ptr)); PyObject* methodObject = PyObject_GetAttrString(vtkObject, method); const int argOffset = 2; int numArgs = msg.GetNumberOfArguments(0) - argOffset; vtkSmartPyObject args(PyTuple_New(numArgs)); for (Py_ssize_t i = 0; i < numArgs; ++i) { int msgArg = i + argOffset; int pyArg = i; vtkClientServerStream::Types type = msg.GetArgumentType(0, msgArg); vtkTypeUInt32 length; msg.GetArgumentLength(0, msgArg, &length); #define array_mappings(call) \ call(int8, vtkTypeInt8) \ call(int16, vtkTypeInt16) \ call(int32, vtkTypeInt32) \ call(int64, vtkTypeInt64) \ call(uint8, vtkTypeUInt8) \ call(uint16, vtkTypeUInt16) \ call(uint32, vtkTypeUInt32) \ call(uint64, vtkTypeUInt64) \ call(float32, vtkTypeFloat32) \ call(float64, vtkTypeFloat64) #define scalar_mappings(call) \ array_mappings(call) \ call(bool, bool) \ call(string, vtkStdString) switch (type) { #define make_array(msgType, cxxType) \ case vtkClientServerStream::msgType##_array: \ { \ PyObject* obj = MakeArrayOf(msg, msgArg, length); \ PyTuple_SET_ITEM(args.GetPointer(), pyArg, obj); \ break; \ } array_mappings(make_array) #undef make_array #define make_scalar(msgType, cxxType) \ case vtkClientServerStream::msgType##_value: \ { \ PyObject* obj = MakeScalar(msg, msgArg); \ PyTuple_SET_ITEM(args.GetPointer(), pyArg, obj); \ break; \ } scalar_mappings(make_scalar) #undef make_scalar #undef scalar_mappings #undef array_mappings case vtkClientServerStream::vtk_object_pointer: { vtkObjectBase* argObj; if (msg.GetArgument(0, msgArg, &argObj) && argObj) { PyObject* obj = vtkPythonUtil::GetObjectFromPointer(argObj); PyTuple_SET_ITEM(args.GetPointer(), pyArg, obj); } else { Py_INCREF(Py_None); PyTuple_SET_ITEM(args.GetPointer(), pyArg, Py_None); } break; } // Streams and ID values are intended to be handled internally; they // should never be passed as arguments. case vtkClientServerStream::stream_value: case vtkClientServerStream::id_value: // If we don't recognize it, pass None as the argument. default: Py_INCREF(Py_None); PyTuple_SET_ITEM(args.GetPointer(), pyArg, Py_None); break; } } vtkSmartPyObject callResult(PyObject_CallObject(methodObject, args)); if (!callResult) { vtkOStrStreamWrapper vtkmsg; vtkStdString pymsg = GetPythonErrorString(); vtkmsg << "Object type: " << ptr->GetClassName() << ", failure when calling method: \"" << method << "\": " << pymsg << ".\n"; result.Reset(); result << vtkClientServerStream::Error << vtkmsg.str() << vtkClientServerStream::End; vtkmsg.rdbuf()->freeze(0); return 0; } ReturnResult(callResult, result); return 1; } //---------------------------------------------------------------------------- int vtkClientServerInterpreter::LoadImpl(const char* moduleName) { if (!vtkPythonInterpreter::IsInitialized()) { return 1; } static std::string const moduleSuffix = "Python"; vtkSmartPyObject module(PyImport_ImportModule((moduleName + moduleSuffix).c_str())); if (!module) { return 1; } vtkSmartPyObject properties(PyObject_Dir(module)); Py_ssize_t size = PyList_Size(properties); for (Py_ssize_t i = 0; i < size; ++i) { PyObject* item = PyList_GetItem(properties, i); const char* name = PyString_AsString(item); if (!strncmp("vtk", name, 3)) { vtkSmartPyObject cls(PyObject_GetAttr(module, item)); PythonCSShimData* newInstanceData = new PythonCSShimData(cls); this->AddNewInstanceFunction(name, NewInstanceCallback, newInstanceData, FreePythonCSShimData); PythonCSShimData* commandData = new PythonCSShimData(cls); this->AddCommandFunction(name, CommandFunctionCallback, commandData, FreePythonCSShimData); } } return 0; }