如何在CPython模块的init方法中引发异常

时间:2016-07-18 14:59:21

标签: python python-2.7 cpython python-internals

我在用CPython编写的库顶部写了一个C扩展名,我找不到如何在init方法中引发异常的解决方案。所以,我把它拆分了,基本上,构造函数会将属性保存到对象中,然后你必须调用init方法mr.initialize()

我发现这个方法有点难看,我想找到一个解决方案来在构造函数中引发ValueError异常,这是我当前的代码:

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;
}

添加的初始化方法如下:

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);
}

CPython doc,如果你想让解释器引发异常,你需要在我的情况下调用用于引发异常的方法之一,我使用PyErr_SetString(PyExc_ValueError, msgError),并返回{{1 }}

在这种情况下,null中的init方法必须是CPython,所以我无法返回static int,但我删除了null语句,我看到了return中的例外,但解释器没有停止。

我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:1)

您应该在libzihc_MRLoader_init中返回一个负值,通常是-1,以便Python捕获它,搜索是否设置了异常并结束执行。至少,在调用对象__init__方法之后,type_call检查了什么:

    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;
    }

因此,在您的具体情况下,您可以从initialize中的libzihc_MRLoader_init移动代码,如果发生错误,则返回-1而非null信号它