Files
ThirdParty-6/ParaView-5.0.1/ParaViewCore/ClientServerStream/vtkClientServerInterpreterPython.cxx

511 lines
15 KiB
C++

/*=========================================================================
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 <typename T>
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<double>(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 <typename T>
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<T>(argArray[i]));
}
delete [] argArray;
return tuple;
}
//----------------------------------------------------------------------------
template <typename T>
static PyObject* MakeScalar(const vtkClientServerStream& msg, int arg)
{
T argValue;
if (!msg.GetArgument(0, arg, &argValue))
{
Py_RETURN_NONE;
}
return PythonConvert<T>(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 = "<Unable to convert Python error to 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<cxxType>(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<cxxType>(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;
}