Python C扩展模块中没有返回值的方法

12

我正在尝试用Python创建一个通过并行端口发送数据的脚本。我正在使用C语言创建自己的模块。

问题是:当我尝试执行我的模块时,Python会崩溃。没有错误,没有数据,什么也没有。它只是关闭了。

这是我的模块:

#include <Python.h>
#include <sys/io.h>
#define BaseAddr 0x378

/*----------------------------------------------------------------------------------
Este es un módulo destinado a controlar el puerto paralelo.
Probablemente tenga que ser ejecutado como administrador.

Created by markmb
------------------------------------------------------------------------------------*/

static PyObject *
paralelo(PyObject *self, PyObject *args){
    int pin;
    ioperm(BaseAddr,3,1);
    if (!PyArg_ParseTuple(args, "i", &pin))
        return NULL;
    outb(pin,BaseAddr);
    ioperm(BaseAddr,3,0);
    return 1
}
PyMethodDef methods[] = {
    {"paralelo", paralelo, METH_VARARGS, "Sends data through a parallel port"},
    {NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initparalelo(void){
    (void) Py_InitModule("paralelo", methods);
}

(它可以在没有所有 Python 混乱的情况下运行) 我通过 distutils 进行编译,然后在终端(使用 Xubuntu)中输入:

import paralelo
while True:
    paralelo.paralelo(255)

并且在这里,它跳出Python,并输出“markmb@...”

提前感谢!


我还没有用C语言制作过自己的Python模块,但是在你的函数“paralelo”中,它应该返回PyObject *,为什么最后返回1? - Lloyd Macrohon
它真的不应该返回任何东西,但我保留了那个返回值,以便在将来编写另一个模块时记得。 - markmb
那么,如果您返回NULL呢?而不是1,这是一个无效的指针。 - Lloyd Macrohon
看起来好多了……但还是会崩溃。现在,在我创建的程序中使用该模块时,它会抛出一个错误:SystemError:error return without exception set。 - markmb
1
顺便提一句,对于这个已经有一个模块了,http://pypi.python.org/pypi/portio。 - Anders Waldenborg
是的,另一个用户也说过,但似乎它已经删除了回答。我找不到它,因为我已经开始了,所以我想完成它。你的“Py_None”解决方案可行(你忘记了下划线)。 - markmb
2个回答

22

所有的Python函数都应该返回一个PyObject对象,除非它们想要抛出异常,如此处所述:http://docs.python.org/extending/extending.html#intermezzo-errors-and-exceptions

你收到的错误信息SystemError: error return without exception set,试图告诉你,你的函数返回了NULL(=错误,抛出异常),但没有告诉Python解释器你想要抛出哪个异常。

当你不想从Python函数中返回值时,可以使其返回None(这与在Python代码中运行到最后或执行简单的return语句而没有任何值相同)。

在CPython API中,你可以通过返回Py_None对象来实现此功能,并记得增加其引用计数。为了帮助你不忘记引用计数,有一个宏可以自动完成: http://docs.python.org/c-api/none.html#Py_RETURN_NONE

因此,一个不返回任何值(即返回None)的函数的框架大致如下:

static PyObject *
myfunction(PyObject *self, PyObject *args){
    if (!PyArg_ParseTuple(args, "i", ...))
        return NULL;
    /* .... */
    Py_RETURN_NONE;
}

最后,需要说明的是:已经有一个用于执行ioperm/outb调用的Python模块了:http://pypi.python.org/pypi/portio


11

在Python/C API中返回NULL表示发生了错误。但是由于您实际上没有设置异常,因此会收到以下错误:

SystemError: error return without exception set

如果您尝试返回None,请使用如下方法:

return Py_BuildValue("");

我使用了其他评论中提到的方法:返回 Py_None,它可以正常工作。 - markmb
4
@markmb,小心!如果你使用return Py_None,你需要增加它的引用计数,否则你会遇到麻烦。你应该使用我提供的方法,或者另一个回答中提到的Py_RETURN_NONE,或者调用Py_IncRef(Py_None)。 - Winston Ewert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接