-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Hello,
I asked quickly on gitter (https://gitter.im/pybind/Lobby?at=5a7b7f1c6117191e610a8304),
but I feel like this may take a bit longer to resolve.
I'd like to bind a custom exception class to Python.
This custom exception class has attributes,
which I want to expose to Python.
This is very similar to the std::system_error,
which has a code() member function:
If this issue is sorted out,
maybe as a result a std::system_error binding could be provided?
There was interests shown here:
My issue right now, is how to inherit a Python built-in type, more precisely, PyExc_RuntimeError.
I'm not yet trying to register a custom translator.
If I attempt to use the code proposed on Gitter:
py::class_<CppException>(m, "PyException",
py::reinterpret_borrow<py::object>(PyExc_RuntimeError))
.def(...)I get an assertion error when importing the module:
python: pybind11/detail/class.h:591:
PyObject* pybind11::detail::make_new_python_type(const pybind11::detail::type_record&):
Assertion `rec.dynamic_attr ? (((type)->tp_flags & ((1L<<14))) != 0)
: !(((type)->tp_flags & ((1L<<14))) != 0)' failed.
I have a little piece of code that I use to understand how things work,
when inheriting a PyObject *.
#include <pybind11/pybind11.h>
namespace py = pybind11;
struct FooBase { int base_value = 444; };
struct Foo // : FooBase // 1. commented on purpose
{
int value = 42;
};
PYBIND11_MODULE(p11, m)
{
auto pyfoobase = py::class_<FooBase>(m, "PyFooBase");
pyfoobase.def(py::init<>());
pyfoobase.def_readonly("base_value", &FooBase::base_value);
PyObject* obj = pyfoobase.ptr();
// obj = PyExc_RuntimeError; // 2. uncomment to get the assertion error
py::class_<Foo>(m, "PyFoo", py::reinterpret_borrow<py::object>(obj))
.def(py::init<>())
.def_readonly("value", &Foo::value);
}If I run this code, everything compiles
even if FooBase is not a base class of Foo.
However, if I run the code, base_value returns 42, not 444:
$ python -c 'import p11; print(p11.PyFoo().base_value)'
42
If I uncomment struct Foo // : FooBase => struct Foo : FooBase,
I get the expected behavior:
$ python -c 'import p11; print(p11.PyFoo().base_value)'
444
It kind of make sense to me,
but I'm wondering what to do if I wanted to use a Python builtin type as a base,
instead of my FooBase class:
- PyObject* obj = pyfoobase.ptr();
+ PyObject* obj = PyExc_RuntimeError;
How does my PyFoo type store the base class data?
In case of FooBase, the data is stored when Foo inherits FooBase.
If I inherit PyExc_RuntimeError, what would be my base class?
I don't really want add anything Python to my base class,
so I assume the right thing to do would be to give this base class information
only to the PyFoo wrapper.
How can I inherit a builtin Python type such as PyExc_RuntimeError?
Related issue: