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 Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig