mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
511 lines
15 KiB
C++
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;
|
|
}
|