I am writing a CPython
extension on top of a library that was written in C
and I couldn't find a solution on how to raise an exception in the init
method. So, I split it up and, basically, the constructor will save the attributes to the object and then you have to call an init method mr.initialize()
.
I find this method a bit ugly, and I want to find a solution to raise a ValueError
exception in the constructor, here is my current code:
static int libzihc_MRLoader_init(libzihc_MRLoader *self, PyObject *args, PyObject *kwds) {
double error_rate;
const char *in;
unsigned int capacity;
static char *kwlist[] = {"in", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &in)) {
return -1;
}
if (self->dataPath) {
free(self->dataPath);
}
self->dataPath = malloc(sizeof(char)*(strlen(in)+1));
if (self->dataPath) {
strcpy(self->dataPath, in);
} else {
PyErr_NoMemory();
}
return 0;
}
And the added initialize method like so:
static PyObject* initialize(libzihc_MRLoader *self, PyObject *args, PyObject *kwds) {
const char * msgError = NULL;
int status = openRes(self->dataPath, &(self->ienaKey), &msgError);
if(status != 0) {
PyErr_SetString(PyExc_ValueError, msgError);
printf("openRes returns %d\n", status);
return (PyObject *) NULL;
}
return Py_BuildValue("i", status);
}
From the CPython
doc, if you want the interpreter to raise the exception you need to call one of the methods used to raise an exception in my case I use PyErr_SetString(PyExc_ValueError, msgError)
, and return null
.
In this case the init method in CPython
have to be static int
, so I cannot return null
, I did, however, remove the return
statement, I saw the exception in stdout
, but the interpreter did not stop.
How can I achieve this ?
You should return a negative value in libzihc_MRLoader_init
, usually -1
for Python to catch it, search if an exception is set and end execution. At least, that's what type_call
checks for after calling an objects __init__
method:
type = obj->ob_type;
if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) &&
type->tp_init != NULL &&
type->tp_init(obj, args, kwds) < 0) { // if res < 0 returns NULL
Py_DECREF(obj);
obj = NULL;
}
So, in your specific case, you can move the code from initialize
in libzihc_MRLoader_init
and, if an error has occurred, return -1
instead of null
to signal it.