Commit 7eb427d2 authored by David Gobbi's avatar David Gobbi Committed by Code Review

Merge topic 'refactor-wrap-python' into master

f7475425 Update the readme file for new wrapping capabilities.
e13d9aea Only wrap namespaces that have useful contents.
3e100545 Avoid creating duplicate wrapped namespaces.
d5cb2382 Wrap constants more efficiently.
a5948d9f Move VTK_BUILD_SHARED_LIBS check to generated code.
67a70a82 Allow use of all enum types as method parameters.
b271e764 Wrap namespaces in python.
57b47954 Fix two previously undetected wrapper bugs.
ba693ade Add an enum type to the python wrappers.
f642f513 Wrap enum constant members for non-vtkObject types.
a73ab651 Refactor vtkWrapPython into smaller source files.
parents dbcc795a f7475425
......@@ -2,6 +2,7 @@ vtk_add_test_python(
NO_DATA NO_VALID NO_OUTPUT
PythonSmoke.py
TestArrayArguments.py
TestEnums.py
TestExecuteMethodFinalizeCrash.py
TestGhost.py
TestIgnoreBTX.py
......
"""Test enum support in VTK-Python
Created on Nov 13, 2014 by David Gobbi
"""
import sys
import exceptions
import vtk
from vtk.test import Testing
class TestEnum(Testing.vtkTest):
def testGlobalNamespaceEnum(self):
"""Check that an enum in the global namespace was wrapped.
"""
# defined in vtkGenericEnSightReader.h
if hasattr(vtk, 'vtkGenericEnsightReader'):
self.assertEqual(vtk.SINGLE_PROCESS_MODE, 0)
self.assertEqual(vtk.SPARSE_MODE, 1)
self.assertEqual(type(vtk.SINGLE_PROCESS_MODE),
vtk.EnsightReaderCellIdMode)
self.assertEqual(type(vtk.SPARSE_MODE),
vtk.EnsightReaderCellIdMode)
def testClassNamespaceEnum(self):
"""Check that an enum in a class namespace was wrapped.
"""
# defined in vtkColorSeries.h
self.assertEqual(vtk.vtkColorSeries.SPECTRUM, 0)
self.assertEqual(type(vtk.vtkColorSeries.SPECTRUM),
vtk.vtkColorSeries.ColorSchemes)
# defined in vtkErrorCode.h
self.assertEqual(vtk.vtkErrorCode.FirstVTKErrorCode, 20000)
self.assertEqual(type(vtk.vtkErrorCode.FirstVTKErrorCode),
vtk.vtkErrorCode.ErrorIds)
def testAnonymousEnum(self):
"""Check that anonymous enums are wrapped.
"""
# defined in vtkAbstractArray.h
self.assertEqual(vtk.vtkAbstractArray.AbstractArray, 0)
if __name__ == "__main__":
Testing.main([(TestEnum, 'test')])
......@@ -8,8 +8,14 @@ set(Module_SRCS
set_source_files_properties(
vtkErrorCode.cxx
vtkPolygonBuilder
vtkPolygonBuilder.cxx
WRAP_EXCLUDE
)
set_source_files_properties(
vtkErrorCode.cxx
vtkPolygonBuilder.cxx
PROPERTIES WRAP_SPECIAL 1
)
vtk_module_library(vtkCommonMisc ${Module_SRCS})
Notes on the Python Wrappers for VTK
First version by David Gobbi: Dec 19, 2002
Last update was on May 30, 2011.
Last update was on Dec 2, 2014
Abstract:
=========
......@@ -93,6 +93,29 @@ module but sometimes as class attributes:
>>> vtk.vtkCommand.ErrorEvent
39
Each named enum type is wrapped as a new Python type, and members
of the enum are instances of that type. This allows type checking
for enum types:
>>> # works if given a constant of the correct enum type
>>> o.SetIntegrationMode(o.Continuous)
>>> # does not work, because "int" is not of the correct type
>>> o.SetIntegrationMode(10)
TypeError: SetIntegrationMode arg 1: expected enum EnumType, got int
Note that members of anonymous enums do not have a special type, and
are simply wrapped as python ints.
Namespaces
----------
Namespaces are currently wrapped in a very limited manner: the only
namespace members that are wrapped are constants and enum types.
There is no wrapping of namespaced classes or functions, or of nested
namespaces. This is likely to be expanded upon when (or if) VTK begins
to make greater use of namespaces.
Unavailable methods
-------------------
......@@ -104,8 +127,7 @@ A method is not wrapped if
2) it returns a pointer that is not a vtkObject pointer, char pointer,
or void pointer, unless the method has an entry in the wrapping
hints file -- again, vtkDataArray methods are an exception
3) its parameter list contains a named enum constant
4) it is an operator method (though many exceptions exist)
3) it is an operator method (though many exceptions exist)
Unavailable classes
......
......@@ -9,6 +9,7 @@ set(Module_SRCS
vtkSmartPyObject.cxx
PyVTKClass.cxx
PyVTKMutableObject.cxx
PyVTKNamespace.cxx
PyVTKObject.cxx
PyVTKSpecialObject.cxx
PyVTKTemplate.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: PyVTKNamespace.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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.
=========================================================================*/
/*-----------------------------------------------------------------------
The PyVTKNamespace was created in Nov 2014 by David Gobbi.
This is a PyModule subclass for wrapping C++ namespaces.
-----------------------------------------------------------------------*/
#include "PyVTKNamespace.h"
#include "vtkPythonUtil.h"
// Silence warning like
// "dereferencing type-punned pointer will break strict-aliasing rules"
// it happens because this kind of expression: (long *)&ptr
// pragma GCC diagnostic is available since gcc>=4.2
#if defined(__GNUC__) && (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=2)
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
//--------------------------------------------------------------------
const char *PyVTKNamespace_Doc =
"A python module that wraps a C++ namespace.\n";
//--------------------------------------------------------------------
static void PyVTKNamespace_Delete(PyObject *op)
{
// remove from the map so that there is no dangling reference
vtkPythonUtil::RemoveNamespaceFromMap(op);
// call the superclass destructor
PyVTKNamespace_Type.tp_base->tp_dealloc(op);
}
//--------------------------------------------------------------------
PyTypeObject PyVTKNamespace_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
(char*)"vtk.namespace", // tp_name
0, // tp_basicsize
0, // tp_itemsize
PyVTKNamespace_Delete, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_string
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
(char*)PyVTKNamespace_Doc, // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
&PyModule_Type, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
0, // tp_alloc
0, // tp_new
0, // tp_free
0, // tp_is_gc
0, // tp_bases
0, // tp_mro
0, // tp_cache
0, // tp_subclasses
0, // tp_weaklist
VTK_WRAP_PYTHON_SUPRESS_UNINITIALIZED
};
//--------------------------------------------------------------------
PyObject *PyVTKNamespace_New(const char *name)
{
// first check to see if this namespace exists
PyObject *self = vtkPythonUtil::FindNamespace(name);
if (self)
{
Py_INCREF(self);
}
else
{
// make sure python has readied the type object
PyType_Ready(&PyVTKNamespace_Type);
// call the allocator provided by python for this type
self = PyVTKNamespace_Type.tp_alloc(&PyVTKNamespace_Type, 0);
// call the superclass init function
PyObject *args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, PyString_FromString(name));
PyVTKNamespace_Type.tp_base->tp_init(self, args, 0);
Py_DECREF(args);
// remember the object for later reference
vtkPythonUtil::AddNamespaceToMap(self);
}
return self;
}
//--------------------------------------------------------------------
PyObject *PyVTKNamespace_GetDict(PyObject *self)
{
return PyModule_GetDict(self);
}
//--------------------------------------------------------------------
const char *PyVTKNamespace_GetName(PyObject *self)
{
return PyModule_GetName(self);
}
/*=========================================================================
Program: Visualization Toolkit
Module: PyVTKNamespace.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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.
=========================================================================*/
/*-----------------------------------------------------------------------
The PyVTKNamespace was created in Nov 2014 by David Gobbi.
This is a PyModule subclass for wrapping C++ namespaces.
-----------------------------------------------------------------------*/
#ifndef __PyVTKNamespace_h
#define __PyVTKNamespace_h
#include "vtkWrappingPythonCoreModule.h" // For export macro
#include "vtkPython.h"
#include "vtkSystemIncludes.h"
extern VTKWRAPPINGPYTHONCORE_EXPORT PyTypeObject PyVTKNamespace_Type;
#define PyVTKNamespace_Check(obj) ((obj)->ob_type == &PyVTKNamespace_Type)
extern "C"
{
VTKWRAPPINGPYTHONCORE_EXPORT
PyObject *PyVTKNamespace_New(const char *name);
VTKWRAPPINGPYTHONCORE_EXPORT
PyObject *PyVTKNamespace_GetDict(PyObject *self);
VTKWRAPPINGPYTHONCORE_EXPORT
const char *PyVTKNamespace_GetName(PyObject *self);
}
#endif
......@@ -957,10 +957,10 @@ void *vtkPythonArgs::GetArgAsSpecialObject(
return r;
}
int vtkPythonArgs::GetArgAsEnum(const char *s, bool &valid)
int vtkPythonArgs::GetArgAsEnum(PyTypeObject *enumtype, bool &valid)
{
PyObject *o = PyTuple_GET_ITEM(this->Args, this->I++);
int i = vtkPythonArgs::GetArgAsEnum(o, s, valid);
int i = vtkPythonArgs::GetArgAsEnum(o, enumtype, valid);
if (!valid)
{
this->RefineArgTypeError(this->I - this->M - 1);
......@@ -968,12 +968,25 @@ int vtkPythonArgs::GetArgAsEnum(const char *s, bool &valid)
return i;
}
int vtkPythonArgs::GetArgAsEnum(PyObject *o, const char *, bool &valid)
int vtkPythonArgs::GetArgAsEnum(
PyObject *o, PyTypeObject *enumtype, bool &valid)
{
// should check enum type for validity
int i = 0;
valid = (vtkPythonGetValue(o, i) != 0);
return (valid ? i : 0);
long i = 0;
if (o->ob_type == enumtype)
{
i = PyInt_AsLong(o);
valid = true;
}
else
{
std::string errstring = "expected enum ";
errstring += enumtype->tp_name;
errstring += ", got ";
errstring += o->ob_type->tp_name;
PyErr_SetString(PyExc_TypeError, errstring.c_str());
valid = false;
}
return i;
}
......
......@@ -162,14 +162,14 @@ public:
// Description:
// Get the next argument as an enum value.
template<class T>
bool GetEnumValue(T &v, const char *enumname) {
bool GetEnumValue(T &v, PyTypeObject *enumtype) {
bool r;
v = static_cast<T>(this->GetArgAsEnum(enumname, r));
v = static_cast<T>(this->GetArgAsEnum(enumtype, r));
return r; }
template<class T>
static bool GetEnumValue(PyObject *o, T &v, const char *enumname) {
static bool GetEnumValue(PyObject *o, T &v, PyTypeObject *enumtype) {
bool r;
v = static_cast<T>(vtkPythonArgs::GetArgAsEnum(o, enumname, r));
v = static_cast<T>(vtkPythonArgs::GetArgAsEnum(o, enumtype, r));
return r; }
// Description:
......@@ -521,9 +521,9 @@ protected:
// Description:
// Get the next argument as an object of the given type.
int GetArgAsEnum(const char *classname, bool &valid);
int GetArgAsEnum(PyTypeObject *enumtype, bool &valid);
static int GetArgAsEnum(
PyObject *o, const char *classname, bool &valid);
PyObject *o, PyTypeObject *enumtype, bool &valid);
// Description:
// Get the next argument as an object of the given type.
......
......@@ -612,6 +612,44 @@ int vtkPythonOverload::CheckArg(
}
}
// Enum type
else if (isalpha(classname[0]) ||
(classname[0] == '&' && isalpha(classname[1])))
{
if (classname[0] == '&')
{
classname++;
}
if (PyInt_Check(arg))
{
if (strcmp(arg->ob_type->tp_name, classname) == 0)
{
penalty = VTK_PYTHON_EXACT_MATCH;
}
else
{
/* tp_name doesn't include namespace, so we also allow
matches between "name" and "namespace.name" */
size_t l, m;
l = strlen(arg->ob_type->tp_name);
m = strlen(classname);
if (l < m && !isalnum(classname[m-l-1]) &&
strcmp(arg->ob_type->tp_name, &classname[m-l]) == 0)
{
penalty = VTK_PYTHON_GOOD_MATCH;
}
else
{
penalty = VTK_PYTHON_NEEDS_CONVERSION;
}
}
}
else
{
penalty = VTK_PYTHON_INCOMPATIBLE;
}
}
// An array
else if (classname[0] == '*')
{
......
......@@ -59,7 +59,7 @@ public:
};
//--------------------------------------------------------------------
// There are five maps associated with the Python wrappers
// There are six maps associated with the Python wrappers
// Map VTK objects to python objects (this is also the cornerstone
// of the vtk/python garbage collection system, because it contains
......@@ -105,6 +105,12 @@ class vtkPythonSpecialTypeMap
{
};
// Keep track of all the C++ namespaces that have been wrapped.
class vtkPythonNamespaceMap
: public std::map<std::string, PyObject*>
{
};
// Keep track of all vtkPythonCommand instances.
class vtkPythonCommandList
: public std::vector<vtkWeakPointer<vtkPythonCommand> >
......@@ -157,6 +163,7 @@ vtkPythonUtil::vtkPythonUtil()
this->GhostMap = new vtkPythonGhostMap;
this->ClassMap = new vtkPythonClassMap;
this->SpecialTypeMap = new vtkPythonSpecialTypeMap;
this->NamespaceMap = new vtkPythonNamespaceMap;
this->PythonCommandList = new vtkPythonCommandList;
}
......@@ -167,6 +174,7 @@ vtkPythonUtil::~vtkPythonUtil()
delete this->GhostMap;
delete this->ClassMap;
delete this->SpecialTypeMap;
delete this->NamespaceMap;
delete this->PythonCommandList;
}
......@@ -804,6 +812,67 @@ void *vtkPythonUtil::GetPointerFromSpecialObject(
return NULL;
}
//--------------------------------------------------------------------
void vtkPythonUtil::AddNamespaceToMap(PyObject *module)
{
if (!PyVTKNamespace_Check(module))
{
return;
}
if (vtkPythonMap == NULL)
{
vtkPythonMap = new vtkPythonUtil();
Py_AtExit(vtkPythonUtilDelete);
}
const char *name = PyVTKNamespace_GetName(module);
// let's make sure it isn't already there
vtkPythonNamespaceMap::iterator i =
vtkPythonMap->NamespaceMap->find(name);
if (i != vtkPythonMap->NamespaceMap->end())
{
return;
}
(*vtkPythonMap->NamespaceMap)[name] = module;
}
//--------------------------------------------------------------------
// This method is called
void vtkPythonUtil::RemoveNamespaceFromMap(PyObject *obj)
{
if (vtkPythonMap && PyVTKNamespace_Check(obj))
{
const char *name = PyVTKNamespace_GetName(obj);
vtkPythonNamespaceMap::iterator it =
vtkPythonMap->NamespaceMap->find(name);
if (it != vtkPythonMap->NamespaceMap->end() &&
it->second == obj)
{
// The map has a pointer to the object, but does not hold a
// reference, therefore there is no decref.
vtkPythonMap->NamespaceMap->erase(it);
}
}
}
//--------------------------------------------------------------------
PyObject *vtkPythonUtil::FindNamespace(const char *name)
{
if (vtkPythonMap)
{
vtkPythonNamespaceMap::iterator it =
vtkPythonMap->NamespaceMap->find(name);
if (it != vtkPythonMap->NamespaceMap->end())
{
return it->second;
}
}
return NULL;
}
//--------------------------------------------------------------------
// mangle a void pointer into a SWIG-style string
char *vtkPythonUtil::ManglePointer(const void *ptr, const char *type)
......
......@@ -21,6 +21,7 @@
#include "vtkPython.h"
#include "PyVTKClass.h"
#include "PyVTKMutableObject.h"
#include "PyVTKNamespace.h"
#include "PyVTKObject.h"
#include "PyVTKSpecialObject.h"
......@@ -30,6 +31,7 @@ class vtkPythonCommandList;
class vtkPythonGhostMap;
class vtkPythonObjectMap;
class vtkPythonSpecialTypeMap;
class vtkPythonNamespaceMap;
class vtkStdString;
class vtkUnicodeString;
class vtkVariant;
......@@ -126,6 +128,20 @@ public:
static void *GetPointerFromSpecialObject(
PyObject *obj, const char *result_type, PyObject **newobj);
// Description:
// Add a wrapped C++ namespace as a python module object. This allows
// the namespace to be retrieved and added to as necessary.
static void AddNamespaceToMap(PyObject *o);
// Description:
// Remove a wrapped C++ namespace from consideration. This is called
// from the namespace destructor.
static void RemoveNamespaceFromMap(PyObject *o);
// Description:
// Return an existing namespace, or NULL if it doesn't exist.
static PyObject *FindNamespace(const char *name);
// Description:
// Utility function to build a docstring by concatenating a series
// of strings until a null string is found.
......@@ -161,6 +177,7 @@ private:
vtkPythonGhostMap *GhostMap;
vtkPythonClassMap *ClassMap;
vtkPythonSpecialTypeMap *SpecialTypeMap;
vtkPythonNamespaceMap *NamespaceMap;
vtkPythonCommandList *PythonCommandList;
friend void vtkPythonUtilDelete();
......
......@@ -71,7 +71,18 @@ if(NOT CMAKE_CROSSCOMPILING)
vtk_compile_tools_target(vtkWrapTcl)
vtk_compile_tools_target(vtkWrapTclInit)
add_executable(vtkWrapPython vtkWrapPython.c)
add_executable(vtkWrapPython
vtkWrapPython.c
vtkWrapPythonClass.c
vtkWrapPythonConstant.c
vtkWrapPythonEnum.c
vtkWrapPythonMethod.c
vtkWrapPythonMethodDef.c
vtkWrapPythonNamespace.c
vtkWrapPythonOverload.c
vtkWrapPythonTemplate.c
vtkWrapPythonType.c
)
target_link_libraries(vtkWrapPython vtkWrappingTools)
add_executable(vtkWrapPythonInit vtkWrapPythonInit.c)
vtk_compile_tools_target(vtkWrapPython)
......
......@@ -326,8 +326,8 @@ HierarchyInfo *vtkParseHierarchy_ReadFile(const char *filename)
i += skip_space(&line[i]);
n = vtkParse_NameLength(&line[i]);
/* check for enum indicators */
if ((n == 3 && strncmp(&line[i], "int", n)) ||
(n == 4 && strncmp(&line[i], "enum", n)))
if ((n == 3 && strncmp(&line[i], "int", n) == 0) ||
(n == 4 && strncmp(&line[i], "enum", n) == 0))
{
entry->IsEnum = 1;
i += n;
......
......@@ -285,6 +285,28 @@ int vtkWrap_IsConst(ValueInfo *val)
return ((val->Type & VTK_PARSE_CONST) != 0);
}
/* -------------------------------------------------------------------- */
/* Check if the arg type is an enum that is a member of the class */
int vtkWrap_IsEnumMember(ClassInfo *data, ValueInfo *arg)
{
int i;
if (arg->Class)
{
/* check if the enum is a member of the class */
for (i = 0; i < data->NumberOfEnums; i++)
{
EnumInfo *info = data->Enums[i];
if (info->Name && strcmp(arg->Class, info->Name) == 0)
{
return 1;
}
}
}
return 0;
}
/* -------------------------------------------------------------------- */
/* Hints */
......@@ -864,10 +886,13 @@ const char *vtkWrap_GetTypeName(ValueInfo *val)
/* variable declarations */
void vtkWrap_DeclareVariable(
FILE *fp, ValueInfo *val, const char *name, int i, int flags)
FILE *fp, ClassInfo *data, ValueInfo *val, const char *name,
int i, int flags)
{
unsigned int aType;
int j;
const char *typeName;
char *newTypeName = NULL;
if (val == NULL)
{
......@@ -883,7 +908,27 @@ void vtkWrap_DeclareVariable(
return;
}
/* add a couple spaces */
typeName = vtkWrap_GetTypeName(val);
if (vtkWrap_IsEnumMember(data, val))
{
/* use a typedef to work around compiler issues when someone used
the same name for the enum type as for a variable or method */
newTypeName = (char *)malloc(strlen(name) + 16);
if (i >= 0)
{
sprintf(newTypeName, "%s%i_type", name, i);
}
else
{
sprintf(newTypeName, "%s_type", name);
}
fprintf(fp, " typedef %s::%s %s;\n",
data->Name, typeName, newTypeName);
typeName = newTypeName;
}
/* add a couple spaces for indentation*/
fprintf(fp," ");
/* for const * return types, prepend with const */
......@@ -909,7 +954,7 @@ void vtkWrap_DeclareVariable(
}
/* print the type name */
fprintf(fp, "%s ", vtkWrap_GetTypeName(val));
fprintf(fp, "%s ", typeName);
/* indirection */
if ((flags & VTK_WRAP_RETURN) != 0)
......@@ -1004,6 +1049,8 @@ void vtkWrap_DeclareVariable(
{
fprintf(fp, ";\n");
}
free(newTypeName);
}
void vtkWrap_DeclareVariableSize(
......
......@@ -133,6 +133,11 @@ int vtkWrap_IsSpecialType(
int vtkWrap_IsTypeOf(
HierarchyInfo *hinfo, const char *classname, const char *superclass);
/**
* Check if the type of the value is an enum member of the class.
*/
int vtkWrap_IsEnumMember(ClassInfo *data, ValueInfo *arg);