如何将 python 2 C 扩展模块转换为 python 3
How to convert a python 2 C extension module to python 3
我正在尝试编译我定义自定义类型的扩展模块。但是,我为此目的获得了一些 python 2 代码,但我无法终生将其转换为 python 3 模块。
到目前为止,我已经调整了我在书 'Python in a nutshell' 中找到的原始代码,以便尝试通过合并 https://docs.python.org/3/extending/newtypes_tutorial.html,but I 中的一些位来为 python 3 编译它我仍然一事无成。我知道这本书的第 3 版有一个 python 3 版本的代码,但我无法访问该版本的书。
所以,这是模块源代码test.c:
#include "Python.h"
#include "structmember.h"
/* per-instance data structure */
typedef struct {
PyObject_HEAD
int first, second;
} intpair;
static int
intpair_init(PyObject *self, PyObject *args, PyObject *kwds)
{
static char* nams[] = {"first","second",NULL};
int first, second;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "ii", nams, &first, &second))
return -1;
((intpair*)self)->first = first;
((intpair*)self)->second = second;
return 0;
}
static void
intpair_dealloc(PyObject *self)
{
self->ob_type->tp_free(self);
}
static PyObject* nothing_method(PyObject* self, PyObject* args)
{
printf("This does literally nothing!\n");
Py_RETURN_NONE;
}
static PyMemberDef intpair_members[] = {
{"first", T_INT, offsetof(intpair, first), 0, "first item" },
{"second", T_INT, offsetof(intpair, second), 0, "second item" },
{NULL}
};
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
sizeof(intpair), /* tp_basicsize */
0, /* tp_itemsize */
intpair_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT,
"two ints (first,second)",
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
intpair_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
intpair_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
PyObject_Del, /* tp_free */
};
static PyMemberDef Noddy_members[] = {
{"PI", T_INT, 3, 0, "noddy number"},
{NULL, NULL, 0, NULL}
};
static PyMethodDef CosMethods[] =
{
{"does_nothing", nothing_method, METH_VARARGS, "This really does nothing"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef testmodule = {
PyModuleDef_HEAD_INIT,
.m_name = "test",
.m_doc = "Example module that creates an extension type.",
.m_size = -1,
CosMethods,
};
PyMODINIT_FUNC
PyInit_test(void)
{
PyObject *m;
if (PyType_Ready(&t_intpair) < 0)
return NULL;
m = PyModule_Create(&testmodule);
if (m == NULL)
return NULL;
Py_INCREF(&t_intpair);
PyModule_AddObject(m, "Custom", (PyObject *) &t_intpair);
return m;
}
和文件 setup.py:
from distutils.core import setup, Extension
setup(name="test", version="1.0",
ext_modules=[
Extension("test", ["test.c"]),
])
我可以使用 python3 setup.py build_ext --inplace 成功编译这个模块,但是,当我导入这个模块时python 3 我收到以下错误:
Python 3.7.2 (default, Jan 10 2019, 23:51:51)
[GCC 8.2.1 20181127] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: Type does not define the tp_name field.
我不明白这个错误消息,因为在 PyTypeObject 中定义了一个 tp_name 字段 "test.intpair"。我在这里错过了什么?
此外,需要更改此代码以使其针对 python 3 进行编译吗?
谢谢!
我很确定缺陷是行 0, /* tp_internal */
in:
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
如果您搜索此行,互联网上唯一引用它的是这个问题和您正在使用的书 - 它完全不标准,我不知道它来自哪里。我半怀疑Python2的代码是用PyVarObject
然后用tp_internal
填大小而不是PyVarObject_HEAD_INIT
,但是看不到相关页面书...
The current documentation recommends using C99 initialization 喜欢
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0)
.tp_name = "test.intpair",
// etc
};
这样获得属性的正确顺序就不再重要了(任何你没有指定的都是 0 初始化)。
构建您的代码会提供一些关于不匹配类型的非常明确的警告:
warning: initialization of ‘long int’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
"test_mod.intpair", /* tp_name */`
这些你要注意
我正在尝试编译我定义自定义类型的扩展模块。但是,我为此目的获得了一些 python 2 代码,但我无法终生将其转换为 python 3 模块。
到目前为止,我已经调整了我在书 'Python in a nutshell' 中找到的原始代码,以便尝试通过合并 https://docs.python.org/3/extending/newtypes_tutorial.html,but I 中的一些位来为 python 3 编译它我仍然一事无成。我知道这本书的第 3 版有一个 python 3 版本的代码,但我无法访问该版本的书。
所以,这是模块源代码test.c:
#include "Python.h"
#include "structmember.h"
/* per-instance data structure */
typedef struct {
PyObject_HEAD
int first, second;
} intpair;
static int
intpair_init(PyObject *self, PyObject *args, PyObject *kwds)
{
static char* nams[] = {"first","second",NULL};
int first, second;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "ii", nams, &first, &second))
return -1;
((intpair*)self)->first = first;
((intpair*)self)->second = second;
return 0;
}
static void
intpair_dealloc(PyObject *self)
{
self->ob_type->tp_free(self);
}
static PyObject* nothing_method(PyObject* self, PyObject* args)
{
printf("This does literally nothing!\n");
Py_RETURN_NONE;
}
static PyMemberDef intpair_members[] = {
{"first", T_INT, offsetof(intpair, first), 0, "first item" },
{"second", T_INT, offsetof(intpair, second), 0, "second item" },
{NULL}
};
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
sizeof(intpair), /* tp_basicsize */
0, /* tp_itemsize */
intpair_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT,
"two ints (first,second)",
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
intpair_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
intpair_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
PyObject_Del, /* tp_free */
};
static PyMemberDef Noddy_members[] = {
{"PI", T_INT, 3, 0, "noddy number"},
{NULL, NULL, 0, NULL}
};
static PyMethodDef CosMethods[] =
{
{"does_nothing", nothing_method, METH_VARARGS, "This really does nothing"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef testmodule = {
PyModuleDef_HEAD_INIT,
.m_name = "test",
.m_doc = "Example module that creates an extension type.",
.m_size = -1,
CosMethods,
};
PyMODINIT_FUNC
PyInit_test(void)
{
PyObject *m;
if (PyType_Ready(&t_intpair) < 0)
return NULL;
m = PyModule_Create(&testmodule);
if (m == NULL)
return NULL;
Py_INCREF(&t_intpair);
PyModule_AddObject(m, "Custom", (PyObject *) &t_intpair);
return m;
}
和文件 setup.py:
from distutils.core import setup, Extension
setup(name="test", version="1.0",
ext_modules=[
Extension("test", ["test.c"]),
])
我可以使用 python3 setup.py build_ext --inplace 成功编译这个模块,但是,当我导入这个模块时python 3 我收到以下错误:
Python 3.7.2 (default, Jan 10 2019, 23:51:51)
[GCC 8.2.1 20181127] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: Type does not define the tp_name field.
我不明白这个错误消息,因为在 PyTypeObject 中定义了一个 tp_name 字段 "test.intpair"。我在这里错过了什么?
此外,需要更改此代码以使其针对 python 3 进行编译吗?
谢谢!
我很确定缺陷是行 0, /* tp_internal */
in:
PyObject_HEAD_INIT(0) /* tp_head */
0, /* tp_internal */
"test.intpair", /* tp_name */
如果您搜索此行,互联网上唯一引用它的是这个问题和您正在使用的书 - 它完全不标准,我不知道它来自哪里。我半怀疑Python2的代码是用PyVarObject
然后用tp_internal
填大小而不是PyVarObject_HEAD_INIT
,但是看不到相关页面书...
The current documentation recommends using C99 initialization 喜欢
static PyTypeObject t_intpair = {
PyObject_HEAD_INIT(0)
.tp_name = "test.intpair",
// etc
};
这样获得属性的正确顺序就不再重要了(任何你没有指定的都是 0 初始化)。
构建您的代码会提供一些关于不匹配类型的非常明确的警告:
warning: initialization of ‘long int’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
"test_mod.intpair", /* tp_name */`
这些你要注意