C++ Exceptions in Python extensions

eric eric at enthought.com
Wed Jan 16 16:10:35 EST 2002


Hello,

I'm stuck and would appreciate some eyeballs experienced with C++/Python. I
need to
know if the problem I am seeing extensions is a compiler
problem or a problem with weave (www.scipy.org/site_content/weave). Weave
uses
CXX in the C/C++ modules it generates and has been tested on W2K (MSVC and
gcc-
2.95.2), RH 7.1, BSD, and Solaris successfully. However, two sources
reported
errors on Mandrake 8.0 and 8.1 with gcc-2.96 installed (same compiler that
passes all tests on RH 7.1...)  On Mandrake, weave extensions will produce
an
"Aborted" error for some situations:

   [eric at manatee weave-0.2]$ python some_weave_test.py
   --- TESTING ----

   Aborted

After quite a bit of chasing, I've found that whenever I throw an exception
in a routine called by an extension method that is caught within the
extension
method, I get the Aborted error -- but only if the exception argument is a
"complex"
type such as a class.  Integer exceptions work fine.  For example:

    # THIS WORKS
    void throw_int() { throw 1; }

    static PyObject* compiled_func(PyObject*self, PyObject* args)
    {
        try
        {
            throw_int();
        }
     catch( int val)
     {
         // WORKS
            PyErr_SetString (PyExc_TypeError,"int exception caught");
     }
        return NULL
    }

    # THIS FAILS
    void throw_class() { throw PWException(PyExc_TypeError,"class
exception"); }

    static PyObject* compiled_func(PyObject*self, PyObject* args)
    {
        try
        {
            throw_int();
        }
     catch( PWException& e)
     {
         //FAILS
         PyErr_SetString(PyExc_TypeError, e.What());
     }
        return NULL;
    }

A test case using exceptions in a stand-alone C++ program passes without
errors
on Mandrake. I've boiled the problem down to a bare-bones test case that is
included.  To try this out, put them in a directory and try:

   python s2.py build_ext --inplace

The python test works at least on W2K and RH, but not on Mandrake. These
examples use SCXX style exceptions because I was trying different options.
The
same thing happens with CXX exceptions.

thanks for any insight you might have,

eric


################## W2K (MSVC) ##################

C:\home\ej\wrk\mandrake>python
Python 2.1.1 (#20, Jul 20 2001, 01:19:29) [MSC 32 bit (Intel)] on win32
Type "copyright", "credits" or "license" for more information.

C:\home\ej\wrk\mandrake>python s2.py build_ext --inplace

running build_ext
building 'testfn2' extension
C:\Program Files\Microsoft Visual Studio\VC98\BIN\cl.exe /c /nologo /Ox /MD
/W3
/GX -IC:\Python21\Include /Tptestfn2.cxx /Fobuild\temp.win32-2.1\Release
\testfn2.obj
testfn2.cxx
testfn2.cxx(37) : warning C4101: 'val' : unreferenced local variable
C:\Program Files\Microsoft Visual Studio\VC98\BIN\link.exe /DLL /nologo
/INCREME
NTAL:NO /LIBPATH:C:\Python21\libs /EXPORT:inittestfn2
build\temp.win32-2.1\Relea
se\testfn2.obj /OUT:testfn2.pyd
/IMPLIB:build\temp.win32-2.1\Release\testfn2.lib

   Creating library build\temp.win32-2.1\Release\testfn2.lib and object
build\te
mp.win32-2.1\Release\testfn2.exp

--- TESTING ----

caught exception: class exception
IT WORKED!

################## On RH 7.1 ##################

[ej at beowulf2 ~]$ python
Python 2.1.1 (#2, Sep  6 2001, 03:17:03)
[GCC 2.96 20000731 (Red Hat Linux 7.1 2.96-85)] on linux2
Type "copyright", "credits" or "license" for more information.
>>>

[ej at beowulf2 ~]$ cd mandrake
[ej at beowulf2 ~/mandrake]$ python s2.py build_ext --inplace
running build_ext
building 'testfn2' extension
creating build
creating build/temp.linux-i686-2.1
gcc -g -O2 -Wall -Wstrict-prototypes -fPIC
-I/home/emag/ej/include/python2.1 -c testfn2.cxx
-o build/temp.linux-i686-2.1/testfn2.o
g++ -shared build/temp.linux-i686-2.1/testfn2.o -o testfn2.so

--- TESTING ----

caught exception: class exception
IT WORKED!

################## On Mandrake ##################
[eric at manatee weave-0.2]$ python
Python 2.1.1 (#1, Dec 12 2001, 09:47:33)
[GCC 2.96 20000731 (Mandrake Linux 8.1 2.96-0.62mdk)] on linux2

[eric at manatee weave-0.2]$ ./a.out
PWException exceptionclass exception
IT WORKED!

[eric at manatee weave-0.2]$ python s2.py build_ext --inplace
running build_ext
building 'testfn2' extension
/usr/bin/gcc -O3 -minline-all-stringops -fomit-frame-pointer
 -fPIC -I/usr/local/include/python2.1 -c testfn2.cxx
 -o build/temp.linux-i686-2.1/testfn2.o
g++ -shared build/temp.linux-i686-2.1/testfn2.o -o testfn2.so

--- TESTING ----

Aborted

files:

------------------ s2.py -----------------------

#!/usr/bin/env python
import os,sys,glob
from distutils.core import setup, Extension

from distutils.unixccompiler import UnixCCompiler
import distutils.sysconfig

old_init_posix = distutils.sysconfig._init_posix

def _init_posix():
    old_init_posix()
    distutils.sysconfig._config_vars['LDSHARED'] = 'g++ -shared'

distutils.sysconfig._init_posix = _init_posix

if __name__ == '__main__':
    ext = Extension('testfn2', ["testfn2.cxx"])
    setup(name='testfn',ext_modules = [ext])

    print
    print '--- TESTING ----'
    print

    import testfn2

    try:
        testfn2.compiled_func(locals(),globals())
    except TypeError, msg:
        print 'caught exception:', msg
        print 'IT WORKED!'

------------------ testfn2.cxx ------------------------

include "Python.h"
#include <string>
#include <string.h>

class PWException {
protected:
 char m_msg[500];
 PyObject* py_exception;
public:
 PWException() : py_exception(0) { m_msg[0] = '\0';};
 PWException(PyObject* exp, const char* msg) : py_exception(exp) {
  strncpy(m_msg, msg, sizeof m_msg);
 };
 PWException(const char* msg) : py_exception(0) {
  strncpy(m_msg, msg, sizeof m_msg);
 };
 PWException(const PWException& rhs) : py_exception(rhs.py_exception) {
  strncpy(m_msg, rhs.What(), sizeof m_msg);
 };
 ~PWException() {};
 const char* What() const { return m_msg;};
};

void throw_int() { throw 1; }
void throw_string() { throw std::string("string exception"); }
void throw_class() { throw PWException(PyExc_TypeError,"class exception"); }

static PyObject* compiled_func(PyObject*self, PyObject* args)
{
    // This will ALWAYS through a TypeError exception
    try
 {
        //throw_int();
        //throw_string();
        throw_class();
    }
 catch( int val)
 {
     // WORKS
        PyErr_SetString (PyExc_TypeError,"int exception caught");
 }
 catch( std::string& s)
 {
     // FAILS
        PyErr_SetString (PyExc_TypeError,s.c_str());
 }
 catch( PWException& e)
 {
     //FAILS
     PyErr_SetString(PyExc_TypeError, e.What());
 }
    return NULL;
}

static PyMethodDef compiled_methods[] =
{
    {"compiled_func",(PyCFunction)compiled_func , METH_VARARGS},
    {NULL,      NULL}        /* Sentinel */
};


extern "C" void inittestfn2()
{
    (void) Py_InitModule("testfn2", compiled_methods);
}

----------------- test_alone.cxx -------------------
// [eric]$ This works on all 3 platforms.
// [eric]$ g++ test_alone.cxx
// [eric]$ ./a.out
// PWException exceptionclass exception
// IT WORKED!

#include <string>
#include <iostream>
#include <string.h>

class PWException {
protected:
 char m_msg[500];
 int* py_exception;
public:
 PWException() : py_exception(0) { m_msg[0] = '\0';};
 PWException(int* exp, const char* msg) : py_exception(exp) {
  strncpy(m_msg, msg, sizeof m_msg);
 };
 PWException(const char* msg) : py_exception(0) {
  strncpy(m_msg, msg, sizeof m_msg);
 };
 PWException(const PWException& rhs) : py_exception(rhs.py_exception) {
  strncpy(m_msg, rhs.What(), sizeof m_msg);
 };
 ~PWException() {};
 const char* What() const { return m_msg;};
};

void throw_int() { throw 1; }
void throw_string() { throw std::string("string exception"); }
void throw_class() { throw PWException("class exception"); }

int main()
{
    try
 {
        //throw_int();
        // throw_string();
        throw_class();
    }
   catch( PWException& e)
 {
        cout << "PWException exception" <<  e.What() << std::endl;
 }
 catch( std::string& msg)
 {
        cout << "std::string exception" <<  msg << std::endl;
 }
 catch( int msg)
 {
        cout << "int exception" <<  msg << std::endl;
 }
 cout << "IT WORKED!" << std::endl;
 return 0;
}

------------------------------------------

--
Eric Jones <eric at enthought.com>
Enthought, Inc.
(512) 536-1057






More information about the Python-list mailing list