On Tue, 08 Nov 2011, Jim Bosch wrote:
> In the meantime, I've essentially followed the "parallel pure-Python
> exception hierarchy" method, but you can do some metaprogramming
> tricks to keep from having to actually build the Python hierarchy
> yourself. In other words, write some Python code that inspects your
> Boost.Python-wrapped C++ exception hierarchy and calls type(...) to
> make corresponding Python types that derive from Exception. The
> pure-Python exceptions can then be set as class members of the
> Boost.Python-wrapped C++ exceptions, so your translate functions can
> do something like this:
>
> void translateExc(const BaseExc & exc) {
> bp::object wrappedExc(exc);
> bp::object pyExcType = wrappedExc.attr("_pyExcType")
> // assume the constructor for the Python exception accepts a
> // C++ exception; useful if the C++ exception has data
> bp::object pyExc = pyExcType(wrappedExc);
> PyErr_SetObject(pyExcType.ptr(), pyExc.ptr());
> }
>
> To finish it off, you could add a from-Python converter that
> converts the pure-Python exception back to the appropriate C++
> exception when it's passed to Boost.Python-wrapped functions, but
> that isn't always necessary.
>
Thanks, I ended up implementing something along those lines (which
was actually pretty close to what the original Python/C bindings
were doing). I've got the basic funcionality working (exception
translation, from python conversion, to python conversion), but it
still seems a bit fragile, and only supports exceptions with a
single string.
I've attached the latest iteration of the module and testcase below.
If anyone can suggest any improvements, or a way to get it working
with standard boost wrappings of the c++ types, I'd appreciate it.
-nick
exctestmodule.cpp:
#include <Python.h>
#include <boost/python.hpp>
#include <boost/format.hpp>
using namespace boost::python;
namespace ExcTest {
template <class Exc>
struct ExcTranslator
{
static PyObject *pytype;
static const char *module;
static const char *name;
// to python
static PyObject *convert(const Exc &exc)
{
return incref(object(handle<>(borrowed(pytype)))(exc.what()).ptr());
}
static PyTypeObject *get_pytype()
{
return (PyTypeObject *)pytype;
}
// from python
static void *convertible(PyObject *exc)
{
if (!PyType_IsSubtype(Py_TYPE(exc),(PyTypeObject *)pytype)) return 0;
return exc;
}
static void construct(PyObject* raw_exc, converter::rvalue_from_python_stage1_data* data)
{
object exc(handle<>(borrowed(raw_exc)));
std::string s = extract<std::string>(exc.attr("__str__")());
void *storage = ((converter::rvalue_from_python_storage<Exc>*)data)->storage.bytes;
new (storage) Exc(s);
data->convertible = storage;
}
// translate exception
static void translate(const Exc &exc)
{
PyErr_SetObject(pytype,convert(exc));
}
};
template <class Exc, class Base>
void
registerExc()
{
std::string classname = ExcTranslator<Exc>::name;
std::string module = ExcTranslator<Exc>::module;
std::string basename = ExcTranslator<Base>::name;
std::string basemodule = ExcTranslator<Base>::module;
dict tmpDict;
tmpDict["__builtins__"] = handle<>(borrowed(PyEval_GetBuiltins()));
std::string definition;
if (basemodule != module)
{
definition += (boost::format("import %s\n") % basemodule).str();
basename = (boost::format("%s.%s") % basemodule % basename).str();
}
else
{
// bind in the base class type into the tmp dict
tmpDict[basename] = object(handle<>(borrowed(ExcTranslator<Base>::pytype)));
}
definition += (boost::format("class %s (%s):\n"
" def __init__ (self, v=''):\n"
" super(%s,self).__init__(v)\n"
" def __repr__ (self):\n"
" return \"%s.%s('%%s')\"%%(self.args[0])\n")
% classname % basename % classname % module % classname).str();
handle<> tmp(PyRun_String(definition.c_str(),Py_file_input,tmpDict.ptr(),tmpDict.ptr()));
object exc_class = tmpDict[classname];
scope().attr(classname.c_str()) = exc_class;
ExcTranslator<Exc>::pytype = exc_class.ptr();
// to python
to_python_converter<Exc,ExcTranslator<Exc>,true>();
// from python
converter::registry::push_back(&ExcTranslator<Exc>::convertible,
&ExcTranslator<Exc>::construct,type_id<Exc>());
// exception translation
register_exception_translator <Exc>(&ExcTranslator<Exc>::translate);
}
// not registered, but define RuntimeError so that BaseExc can have the appropriate
// python base type
struct RuntimeError {};
template<> PyObject *ExcTranslator<RuntimeError>::pytype = PyExc_RuntimeError;
template<> const char *ExcTranslator<RuntimeError>::name = "RuntimeError";
template<> const char *ExcTranslator<RuntimeError>::module = "__builtin__";
struct BaseExc : public std::exception
{
explicit BaseExc(const std::string &message) : _message(message) {}
virtual ~BaseExc() throw() {}
virtual const char *what() const throw() { return _message.c_str(); }
std::string _message;
};
struct ArgExc : public BaseExc
{
explicit ArgExc(const std::string &message) : BaseExc(message) {}
virtual ~ArgExc() throw() {}
};
void
testException (int idx)
{
if (idx == 1)
throw ArgExc("ArgExc from c++");
throw BaseExc("BaseExc from c++");
}
#define PY_DEFINE_EXC(ExcType,ModuleName,ExcName) \
template <> PyObject *ExcTranslator<ExcType>::pytype = 0; \
template <> const char *ExcTranslator<ExcType>::module = #ModuleName; \
template <> const char *ExcTranslator<ExcType>::name = #ExcName;
PY_DEFINE_EXC(BaseExc,exctest,BaseExc)
PY_DEFINE_EXC(ArgExc,exctest,ArgExc)
std::string
baseExcString(const BaseExc &exc)
{
return exc.what();
}
std::string
argExcString(const ArgExc &exc)
{
return exc.what();
}
BaseExc
makeBaseExc(const std::string &s)
{
return BaseExc(s);
}
ArgExc
makeArgExc(const std::string &s)
{
return ArgExc(s);
}
} // namespace ExcTest
using namespace ExcTest;
BOOST_PYTHON_MODULE(exctest)
{
def("testException", &testException);
def("baseExcString", &baseExcString);
def("argExcString", &argExcString);
def("makeBaseExc", &makeBaseExc);
def("makeArgExc", &makeArgExc);
registerExc<BaseExc,RuntimeError>();
registerExc<ArgExc,BaseExc>();
}
testexc.py:
#!/usr/bin/env python2.6
import exctest
import traceback
print 'testing BaseExc exception creation:'
e1 = exctest.BaseExc('BaseExc from python')
assert str(e1) == 'BaseExc from python'
assert repr(e1) == "exctest.BaseExc('BaseExc from python')"
#assert isinstance(e1,RuntimeError)
#del e1
print ' pass: %s' % (repr(e1))
print 'testing ArgExc exception creation:'
e2 = exctest.ArgExc('ArgExc from python')
assert str(e2) == 'ArgExc from python'
assert repr(e2) == "exctest.ArgExc('ArgExc from python')"
#assert isinstance(e2,RuntimeError)
assert isinstance(e2,exctest.BaseExc)
#del e2
print ' pass: %s' % (repr(e2))
print 'testing raising existing BaseExc exception object:'
try:
raise e1
except exctest.ArgExc, e:
traceback.print_exc()
assert False
except exctest.BaseExc, e:
print ' pass: %s' % (repr(e))
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing raising existing ArgExc exception object:'
try:
raise e2
except exctest.ArgExc, e:
print ' pass: %s' % (repr(e))
except exctest.BaseExc, e:
traceback.print_exc()
assert False
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing BaseExc exception translation:'
try:
exctest.testException(0)
except exctest.ArgExc, e:
traceback.print_exc()
assert False
except exctest.BaseExc, e:
print ' pass: %s' % (repr(e))
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing ArgExc exception translation:'
try:
exctest.testException(1)
except exctest.ArgExc, e:
print ' pass: %s' % (repr(e))
except exctest.BaseExc, e:
traceback.print_exc()
assert False
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing BaseExc raise:'
try:
raise exctest.BaseExc('new BaseExc from python')
except exctest.ArgExc, e:
traceback.print_exc()
assert False
except exctest.BaseExc, e:
print ' pass: %s' % (repr(e))
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing ArgExc raise:'
try:
raise exctest.ArgExc('new ArgExc from python')
except exctest.ArgExc, e:
print ' pass: %s' % (repr(e))
except exctest.BaseExc, e:
traceback.print_exc()
assert False
except:
traceback.print_exc()
assert False
else:
assert False
print 'testing exception conversion:'
be = exctest.makeBaseExc('testStr')
assert (isinstance(be,exctest.BaseExc))
assert (be.__class__ == exctest.BaseExc)
ae = exctest.makeArgExc('testStr')
assert (isinstance(ae,exctest.BaseExc))
assert (isinstance(ae,exctest.ArgExc))
assert (ae.__class__ == exctest.ArgExc)
assert (exctest.baseExcString(be) == 'testStr')
assert (exctest.baseExcString(ae) == 'testStr')
assert (exctest.argExcString(ae) == 'testStr')
print "done"
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig