[Insight-users] Getting PyObject* through to wrapped C++ code
Charl P. Botha
c . p . botha at ewi . tudelft . nl
Tue, 22 Jul 2003 23:19:47 +0200
--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Dear Bill and list,
On Tue, Jul 22, 2003 at 02:33:40PM -0400, Bill Hoffman wrote:
> Please try a proof of concept.
It's worked better than we expected. There is now an itk.swg file in
Insight/Wrapping/CSwig. It's taking care of the PyObject* mapping, as well
as the actual "throws" typemap for the std::exception exception. The amount
of code now left in CableSwig with regards to this is minimal: only
attaching the "throws" attribute to the method node. This should remain, as
eventually gcc_xml will give CableSwig correct throws type information.
What I did was to move the "-l" library handling out of the NoCable clause
of cableSwigMain and adapted all the Wrapping/CSwig top-level CMakeLists.txt
file to make use of this by passing in itk.swg. The advantage of this, is
that we can pass in more swig files than just itk.swg: e.g. per package or
per-class specific interface files!
I've attached a bunch of patches and new files. It was a bit difficult to
separate out the PyCommand changes, so they're included. I'm hoping they
can go in with this lot, as they also constitute a nice test-case for the
new functionality.
So, here we go:
1. CableSwig-itk.swg.diff - a patch to the top-level CableSwig directory
which adapts cableSwigMain.cxx to enable library files and backs out most of
my "throws" typemap code from CableSwig.cxx, as this is now implicitly in
itk.swg.
2. itk.swg - this new file must go in Insight/Wrapping/CSwig (remember to
cvs add ;) It handles exceptions for all languages now, not only Python and
Tcl8.
3. Insight-itk.swg-PyCommand.diff - a patch to top-level Insight that adapts
the Wrapping/CSwig/CmakeLists files so that cswig makes use of itk.swg.
This patch also adapts Wrapping/CSwig/Common/CMakeLists to integrate the
PyCommand class.
4. wrap_ITKPyUtils.cxx, itkPyCommand.h and itkPyCommand.cxx - these should
be copied into Wrapping/CSwig/Common and cvs add'ed. They enable the use of
AddObserver from Python.
5. cannyEdgeDetectionImageFilter-PyCommand.py - this illustrates the use of
PyCommand and also of the new exception handling if the filename isn't
correctly set. :)
I hope that this is satisfactory.
Thanks,
Charl
--
charl p. botha http://cpbotha . net/ http://visualisation . tudelft . nl/
--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="CableSwig-itk.swg.diff"
Index: Executables/CableSwig.cxx
===================================================================
RCS file: /cvsroot/CableSwig/CableSwig/Executables/CableSwig.cxx,v
retrieving revision 1.8
diff -u -r1.8 CableSwig.cxx
--- Executables/CableSwig.cxx 22 Jul 2003 17:04:14 -0000 1.8
+++ Executables/CableSwig.cxx 22 Jul 2003 20:55:13 -0000
@@ -318,40 +318,23 @@
// The following clause will instruct SWIG to let standard C++
// exceptions propagate through as Python exceptions. Original
// version contributed by Charl P. Botha <cpbotha [AT] ieee.org>
-
+ //
// attaching this node as "throws" attribute to the method object
// will cause the swig emit code to add an exception handler the
// node only has to have a type-attribute with the type of the
- // object that will be thrown
+ // object that will be thrown - in itk.swg the actual typemaps
+ // live that map from the type of the throws to the catch code
+ // that will actually be emited
+ //
+ // As soon as CableSwig gets proper throw() types from gcc_xml,
+ // this little piece of code has to be adapted ever so slightly.
Node *catchNode = NewHash();
+ // for now we assume that every method could throw a std::exception
Setattr(catchNode, "type", "std::exception");
Setattr(m, "throws", catchNode);
-
- // this has to be added to the swig typemap... when hitting a method
- // with a "throws" attribute, it will look up the "type" attribute
- // of that "throws" attribute, once again look up the "throws" of
- // that result, and use the "code" attribute of the final result as
- // handler (catch) code - we should only have to do this
- // registration once, but for now it's much clearer here and it does
- // no real harm being called multiple times
- if (m_WrapLanguage == "python")
- {
- char code[] =
- "PyErr_SetString(PyExc_RuntimeError, _e.what());\n"
- "SWIG_fail;";
- Swig_typemap_register("throws", catchNode, code, NULL, NULL);
-
- }
- else if(m_WrapLanguage == "tcl8")
- {
- char code[] =
- "Tcl_SetObjResult(interp, Tcl_NewStringObj(_e.what(), -1));\n"
- "SWIG_fail;";
- Swig_typemap_register("throws", catchNode, code, NULL, NULL);
- }
// take care of the memory
Delete(catchNode);
-
+
ParmList* parms = 0;
Parm* pp = 0;
std::string allParams;
@@ -1162,7 +1145,7 @@
appendChild(top, header);
appendChild(top, init);
}
-
+
// collect up all classes to be included or imported
this->DetermineClassesToWrap(cns);
// first process imported classes
Index: Executables/cableSwigMain.cxx
===================================================================
RCS file: /cvsroot/CableSwig/CableSwig/Executables/cableSwigMain.cxx,v
retrieving revision 1.5
diff -u -r1.5 cableSwigMain.cxx
--- Executables/cableSwigMain.cxx 28 May 2003 21:55:34 -0000 1.5
+++ Executables/cableSwigMain.cxx 22 Jul 2003 20:55:13 -0000
@@ -543,11 +543,14 @@
if(NoCable)
{
Printf(fs,"%%include \"%s\"\n", Swig_last_file());
- for (i = 0; i < Len(libfiles); i++)
- {
- Printf(fs,"\n%%include \"%s\"\n", Getitem(libfiles,i));
- }
}
+ // But still do the -l libraries, as these are used to perform some
+ // application-specific changes
+ for (i = 0; i < Len(libfiles); i++)
+ {
+ Printf(fs,"\n%%include \"%s\"\n", Getitem(libfiles,i));
+ }
+
Seek(fs,0,SEEK_SET);
cpps = Preprocessor_parse(fs);
if (Swig_error_count()) {
--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="itk.swg"
/* This is an itk-specific typemap used by CableSwig. Also see comments
* and "throws" code in CableSwig.cxx.
* -- Charl P. Botha <cpbotha AT ieee.org>
*/
#ifdef SWIGPYTHON
/* ------------------------------------------------------------
* PyObject * - Just pass straight through unmodified
* This is default behaviour for python.swg, but Cable passes
* a PyObject * through as a "p._object", so we redo the typemap
* ------------------------------------------------------------ */
%typemap(in) p._object "$1 = $input;";
%typemap(out) p._object "$result = $1;";
#endif
%include exception.i
/* A "throws" attribute with the "std::exception" type is added synthetically
* to each method node by CableSwig.cxx. When gcc_xml starts passing through
* correct throws types, this typemap could be optionally extended to
* account for more different types. This should work fine for now though.
*/
%typemap(throws) std::exception {
SWIG_exception(SWIG_RuntimeError, _e.what());
}
--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="Insight-itk.swg-PyCommand.diff"
Index: Wrapping/CSwig/CMakeLists.txt
===================================================================
RCS file: /cvsroot/Insight/Insight/Wrapping/CSwig/CMakeLists.txt,v
retrieving revision 1.18
diff -u -r1.18 CMakeLists.txt
--- Wrapping/CSwig/CMakeLists.txt 14 Jul 2003 12:51:07 -0000 1.18
+++ Wrapping/CSwig/CMakeLists.txt 22 Jul 2003 21:01:23 -0000
@@ -172,7 +172,8 @@
COMMENT "${OutputTclCxx} from "
SOURCE ${Bin}/${InputIdx}
COMMAND ${CSWIG}
- ARGS -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
+ ARGS -l${ITK_TOP}/Wrapping/CSwig/itk.swg
+ -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
-o ${Bin}/${OutputTclCxx} -tcl -pkgversion "${ITK_VERSION_STRING}" -c++ ${Bin}/${InputXml}
TARGET ${Library}
OUTPUTS ${Bin}/${OutputTclCxx}
@@ -189,7 +190,8 @@
COMMENT "${OutputTclCxx} from "
SOURCE ${Bin}/${InputIdx}
COMMAND ${CSWIG}
- ARGS -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
+ ARGS -l${ITK_TOP}/Wrapping/CSwig/itk.swg
+ -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
-bindir "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}"
-o ${Bin}/${OutputTclCxx} -python -c++ ${Bin}/${InputXml}
TARGET ${Library}
@@ -207,7 +209,8 @@
COMMENT "${OutputTclCxx} from "
SOURCE ${Bin}/${InputIdx}
COMMAND ${CSWIG}
- ARGS -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
+ ARGS -l${ITK_TOP}/Wrapping/CSwig/itk.swg
+ -c ${CINDEX} ${IGNORE_WARNINGS} -depend ${Bin}/${InputXml}.depend
-bindir "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}"
-o ${Bin}/${OutputTclCxx} -java -c++ ${Bin}/${InputXml}
TARGET ${Library}
Index: Wrapping/CSwig/Common/CMakeLists.txt
===================================================================
RCS file: /cvsroot/Insight/Insight/Wrapping/CSwig/Common/CMakeLists.txt,v
retrieving revision 1.15
diff -u -r1.15 CMakeLists.txt
--- Wrapping/CSwig/Common/CMakeLists.txt 10 Jul 2003 19:34:59 -0000 1.15
+++ Wrapping/CSwig/Common/CMakeLists.txt 22 Jul 2003 21:01:23 -0000
@@ -41,7 +41,7 @@
SET(INDEX_FILE_CONTENT "${INDEX_FILE_CONTENT}${WrapITK_BINARY_DIR}/Common/${Source}.idx\n")
ENDFOREACH(Source)
SET(WRAP_TCL_SOURCES ${WRAP_TCL_SOURCES} wrap_ITKCommonTclTcl.cxx wrap_ITKUtilsTcl.cxx)
-SET(WRAP_PYTHON_SOURCES ${WRAP_PYTHON_SOURCES} wrap_ITKCommonPythonPython.cxx)
+SET(WRAP_PYTHON_SOURCES ${WRAP_PYTHON_SOURCES} wrap_ITKCommonPythonPython.cxx wrap_ITKPyUtilsPython.cxx)
CONFIGURE_FILE(
${WrapITK_SOURCE_DIR}/Master.mdx.in
@@ -72,9 +72,10 @@
ENDIF(ITK_CSWIG_TCL)
-IF(ITK_CSWIG_PYTHON)
+IF(ITK_CSWIG_PYTHON)
+ SET(SWIG_INC ${SWIG_INC} -I${PYTHON_INCLUDE_PATH})
SET_SOURCE_FILES_PROPERTIES(SwigExtras_wrapPython.cxx GENERATED)
- ADD_LIBRARY(_ITKCommonPython MODULE ${WRAP_PYTHON_SOURCES} itkStringStream.cxx SwigExtras_wrapPython.cxx )
+ ADD_LIBRARY(_ITKCommonPython MODULE ${WRAP_PYTHON_SOURCES} itkStringStream.cxx itkPyCommand.cxx SwigExtras_wrapPython.cxx )
ADD_CUSTOM_COMMAND(
COMMENT "run native swig on SwigExtras.i"
SOURCE ${ITK_TOP}/Wrapping/CSwig/Common/SwigExtras.i
@@ -150,5 +151,7 @@
# python
WRAP_PYTHON_SOURCES(${ITK_TOP}/Wrapping/CSwig/Common ${WrapITK_BINARY_DIR}/Common
wrap_ITKCommonPython _ITKCommonPython "${MASTER_INDEX_FILES}" "${ALL_IDX_FILES}")
+ WRAP_PYTHON_SOURCES(${ITK_TOP}/Wrapping/CSwig/Common ${WrapITK_BINARY_DIR}/Common
+ wrap_ITKPyUtils _ITKCommonPython "${MASTER_INDEX_FILES}" "${ALL_IDX_FILES}")
ENDIF(ITK_CSWIG_PYTHON)
Index: Wrapping/CSwig/Common/wrap_ITKCommon.cxx
===================================================================
RCS file: /cvsroot/Insight/Insight/Wrapping/CSwig/Common/wrap_ITKCommon.cxx,v
retrieving revision 1.9
diff -u -r1.9 wrap_ITKCommon.cxx
--- Wrapping/CSwig/Common/wrap_ITKCommon.cxx 14 Jul 2003 12:30:49 -0000 1.9
+++ Wrapping/CSwig/Common/wrap_ITKCommon.cxx 22 Jul 2003 21:01:23 -0000
@@ -49,6 +49,9 @@
#ifdef ITK_TCL_WRAP
ITK_WRAP_GROUP(ITKUtils),
#endif
+#ifdef ITK_PYTHON_WRAP
+ ITK_WRAP_GROUP(ITKPyUtils),
+#endif
"SwigExtras",
ITK_WRAP_GROUP(itkVector),
ITK_WRAP_GROUP(itkVersorTransform)
Index: Wrapping/CSwig/Common/wrap_ITKCommonPython.cxx
===================================================================
RCS file: /cvsroot/Insight/Insight/Wrapping/CSwig/Common/wrap_ITKCommonPython.cxx,v
retrieving revision 1.1
diff -u -r1.1 wrap_ITKCommonPython.cxx
--- Wrapping/CSwig/Common/wrap_ITKCommonPython.cxx 13 May 2003 20:28:38 -0000 1.1
+++ Wrapping/CSwig/Common/wrap_ITKCommonPython.cxx 22 Jul 2003 21:01:24 -0000
@@ -1,2 +1,3 @@
#define ITK_WRAP_PACKAGE "ITKCommonPython"
+#define ITK_PYTHON_WRAP
#include "wrap_ITKCommon.cxx"
--HlL+5n6rz5pIUxbD
Content-Type: text/x-c++src; charset=us-ascii
Content-Disposition: attachment; filename="wrap_ITKPyUtils.cxx"
/*=========================================================================
Program: Insight Segmentation & Registration Toolkit
Module: $RCSfile: wrap_ITKUtils.cxx,v $
Language: C++
Date: $Date: 2003/06/24 22:17:07 $
Version: $Revision: 1.2 $
Copyright (c) 2002 Insight Consortium. All rights reserved.
See ITKCopyright.txt or http://www . itk . org/HTML/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 notices for more information.
=========================================================================*/
#include "itkPyCommand.h"
#ifdef CABLE_CONFIGURATION
#include "itkCSwigMacros.h"
namespace _cable_
{
const char* const group = ITK_WRAP_GROUP(ITKPyUtils);
namespace wrappers
{
ITK_WRAP_OBJECT(PyCommand);
}
}
#endif
--HlL+5n6rz5pIUxbD
Content-Type: text/x-chdr; charset=us-ascii
Content-Disposition: attachment; filename="itkPyCommand.h"
/*=========================================================================
Program: Insight Segmentation & Registration Toolkit
Module: $RCSfile: itkPyCommand.h,v $
Language: C++
Date: $Date: 2003/06/06 15:04:28 $
Version: $Revision: 1.1 $
Copyright (c) 2002 Insight Consortium. All rights reserved.
See ITKCopyright.txt or http://www . itk . org/HTML/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 notices for more information.
=========================================================================*/
#ifndef _itkPyCommand_h
#define _itkPyCommand_h
#include "itkCommand.h"
#include <Python.h>
namespace itk
{
/** \Class PyCommand
* \brief Command subclass that calls a Python callable object, e.g.
* a Python function.
*
* With this class, arbitrary Python callable objects (e.g. functions)
* can be associated with an instance to be used in AddObserver calls.
* This is analogous to itk::TclCommand, but then a tad more flexible. ;)
*
* This class was contributed by Charl P. Botha <cpbotha |AT| ieee.org>
*/
class PyCommand : public Command
{
public:
///! Standard "Self" typedef.
typedef PyCommand Self;
///! Smart pointer typedef support.
typedef SmartPointer<Self> Pointer;
///! Run-time type information (and related methods).
itkTypeMacro(PyCommand,Command);
///! Method for creation through the object factory.
itkNewMacro(Self);
void SetCommandCallable(PyObject *obj);
void Execute(Object *, const EventObject&);
void Execute(const Object *, const EventObject&);
protected:
PyCommand();
~PyCommand();
void PyExecute();
PyCommand(const Self&); // Not implemented.
void operator=(const Self&); // Not implemented.
private:
PyObject *obj;
};
} // namespace itk
#endif // _itkPyCommand_h
--HlL+5n6rz5pIUxbD
Content-Type: text/x-c++src; charset=us-ascii
Content-Disposition: attachment; filename="itkPyCommand.cxx"
/*=========================================================================
Program: Insight Segmentation & Registration Toolkit
Module: $RCSfile: itkPyCommand.cxx,v $
Language: C++
Date: $Date: 2003/06/06 15:04:28 $
Version: $Revision: 1.1 $
Copyright (c) 2002 Insight Consortium. All rights reserved.
See ITKCopyright.txt or http://www . itk . org/HTML/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 notices for more information.
=========================================================================*/
#include "itkPyCommand.h"
namespace itk
{
PyCommand::PyCommand()
{
this->obj = NULL;
}
PyCommand::~PyCommand()
{
if (this->obj)
{
Py_DECREF(this->obj);
}
this->obj = NULL;
}
void PyCommand::SetCommandCallable(PyObject *obj)
{
this->obj = obj;
}
///! Execute the callback to the Tcl interpreter.
void PyCommand::Execute(Object *, const EventObject&)
{
this->PyExecute();
}
///! Execute the callback to the Tcl interpreter with a const LightObject
void PyCommand::Execute(const Object*, const EventObject&)
{
this->PyExecute();
}
void PyCommand::PyExecute()
{
PyObject *result;
result = PyEval_CallObject(this->obj, (PyObject *)NULL);
if (result)
{
Py_DECREF(result);
}
else
{
PyErr_Print();
}
}
} // namespace itk
--HlL+5n6rz5pIUxbD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="cannyEdgeDetectionImageFilter-PyCommand.py"
from InsightToolkit import *
reader = itkImageFileReaderF2_New()
canny = itkCannyEdgeDetectionImageFilterF2F2_New()
def progressCallback():
print canny.GetProgress()
command = itkPyCommand_New()
command.SetCommandCallable(progressCallback)
canny.AddObserver(itkProgressEvent(), command.GetPointer())
rescaler = itkRescaleIntensityImageFilterF2US2_New()
writer = itkImageFileWriterUS2_New()
canny.SetInput(reader.GetOutput())
rescaler.SetInput(canny.GetOutput())
writer.SetInput(rescaler.GetOutput())
rescaler.SetOutputMinimum(0)
rescaler.SetOutputMaximum(65535)
reader.SetFileName("/home/cpbotha/build/Insight/Testing/Data/Input/cthead1.png")
writer.SetFileName("./testout.png")
writer.Update()
--HlL+5n6rz5pIUxbD--