Python将扩展模块移植到Python3

python lib

作者

Benjamin Peterson

摘要

尽管更改 C-API 并不是 Python 3 的目标之一,但许多 Python 级别的更改使得 Python 2 的 API 无法完整实现。实际上,一些变化如 int()long() 的统一在 C 级别更明显。本文档致力于记录不兼容性以及如何解决这些问题。

条件编译¶

仅编译 Python 3 的一些代码的最简单方法是检查 PY_MAJOR_VERSION 是否大于或等于3。

#if PY_MAJOR_VERSION >= 3

#define IS_PY3K

#endif

不存在的 API 函数可以在条件块中别名为它们的等价物。

对象API的更改¶

Python 3 将一些具有类似功能的类型合并在一起,同时干净地分离了其他类型。

str/unicode 统一¶

Python 3 的 str() 类型相当于 Python 2 的 unicode() ; C函数被称为 PyUnicode_* 。旧的 8 位字符串类型变为 bytes() ,其中 C 函数称为 PyBytes_* 。 Python 2.6 及更高版本提供了一个兼容性头文件 bytesobject.h ,将 PyBytes 名称映射到 PyString 。 为了保持与 Python 3 的最佳兼容性, PyUnicode 应该用于文本数据,并且 PyBytes 用于二进制数据。同样重要的是要记住 pyBytes 和 Python 3中的 PyUnicode 不可互换,如 PyStringPyUnicode 在 Python 2 以下中示例显示了以下方面的最佳实践 PyUnicodePyStringPyBytes 。:

#include"stdlib.h"

#include"Python.h"

#include"bytesobject.h"

/* text example */

staticPyObject*

say_hello(PyObject*self,PyObject*args){

PyObject*name,*result;

if(!PyArg_ParseTuple(args,"U:say_hello",&name))

returnNULL;

result=PyUnicode_FromFormat("Hello, %S!",name);

returnresult;

}

/* just a forward */

staticchar*do_encode(PyObject*);

/* bytes example */

staticPyObject*

encode_object(PyObject*self,PyObject*args){

char*encoded;

PyObject*result,*myobj;

if(!PyArg_ParseTuple(args,"O:encode_object",&myobj))

returnNULL;

encoded=do_encode(myobj);

if(encoded==NULL)

returnNULL;

result=PyBytes_FromString(encoded);

free(encoded);

returnresult;

}

long/int 统一¶

Python 3 只有一个整数类型, int() 。但它实际上对应于Python 2 long() 类型 —— 删除了 Python 2 中使用的 int() 类型。在 C-API 中, PyInt_* 函数被它们等价的 PyLong_* 替换。

模块初始化和状态¶

Python 3 有一个改进的扩展模块初始化系统。(参见 PEP 3121 。)而不是将模块状态存储在全局变量中,它们应该存储在特定于解释器的结构中。创建在 Python 2 和 Python 3 中正确运行的模块非常棘手。以下简单示例演示了如何操作。:

#include"Python.h"

structmodule_state{

PyObject*error;

};

#if PY_MAJOR_VERSION >= 3

#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))

#else

#define GETSTATE(m) (&_state)

staticstructmodule_state_state;

#endif

staticPyObject*

error_out(PyObject*m){

structmodule_state*st=GETSTATE(m);

PyErr_SetString(st->error,"something bad happened");

returnNULL;

}

staticPyMethodDefmyextension_methods[]={

{"error_out",(PyCFunction)error_out,METH_NOARGS,NULL},

{NULL,NULL}

};

#if PY_MAJOR_VERSION >= 3

staticintmyextension_traverse(PyObject*m,visitprocvisit,void*arg){

Py_VISIT(GETSTATE(m)->error);

return0;

}

staticintmyextension_clear(PyObject*m){

Py_CLEAR(GETSTATE(m)->error);

return0;

}

staticstructPyModuleDefmoduledef={

PyModuleDef_HEAD_INIT,

"myextension",

NULL,

sizeof(structmodule_state),

myextension_methods,

NULL,

myextension_traverse,

myextension_clear,

NULL

};

#define INITERROR return NULL

PyMODINIT_FUNC

PyInit_myextension(void)

#else

#define INITERROR return

void

initmyextension(void)

#endif

{

#if PY_MAJOR_VERSION >= 3

PyObject*module=PyModule_Create(&moduledef);

#else

PyObject*module=Py_InitModule("myextension",myextension_methods);

#endif

if(module==NULL)

INITERROR;

structmodule_state*st=GETSTATE(module);

st->error=PyErr_NewException("myextension.Error",NULL,NULL);

if(st->error==NULL){

Py_DECREF(module);

INITERROR;

}

#if PY_MAJOR_VERSION >= 3

returnmodule;

#endif

}

CObject 替换为 Capsule¶

Capsule 对象是在 Python 3.1 和 2.7 中引入的,用于替换 CObject 。 CObject 是有用的,但是 CObject API 是有问题的:它不允许区分有效的 CObject ,这导致不匹配的 CObject 使解释器崩溃,并且它的一些 API 依赖于 C 中的未定义行为。有关 Capsule 背后的基本原理的进一步阅读,请参阅 bpo-5630 。)

如果你当前正在使用 CObject ,并且想要迁移到 3.1 或更高版本,则需要切换到 Capsules 。 CObject 在 3.1 和 2.7 中已弃用,在 Python 3.2 中已完全删除。如果你只支持 2.7 或 3.1 及以上,你可以简单地切换到 Capsule 。如果你需要支持 Python 3.0 或早于 2.7 的 Python 版本,则必须同时支持 CObject 和 Capsule 。(请注意,不再支持 Python 3.0 ,不建议将其用于生产用途。)

以下示例头文件 capsulethunk.h 可以为你解决问题。只需针对 Capsule API 编写代码,并在以下文件后包含此头文件 Python.h 。你的代码将自动在带有 Capsule 的 Python 版本中使用 Capsules ,并在 Capsule 不可用时切换到 CObjects 。

capsulethunk.h 使用 CObject 模拟 Capsules 。 但是, CObject 没有提供存储胶囊的“名称”的地方。因此,模拟 Capsule 对象由 capsulethunk.h 创建,其行为与真实 Capsule 略有不同。特别地:

  • 传递给 PyCapsule_New() 的 name 参数被忽略。

  • 传入以下命令的 name 参数 PyCapsule_IsValid()PyCapsule_GetPointer() 被忽略,并且不执行错误检查。

  • PyCapsule_GetName() 总是返回 NULL 。

  • PyCapsule_SetName() 总是引发异常并返回失败。(由于无法在 CObject 中存储名称,因此 PyCapsule_SetName() 的明显失败被认为优于静默失败。如果这样不方便,请随意根据需要修改本地副本。)

你可以在 Python 源代码分发中的 Doc/includes/capsulethunk.h 找到 capsulethunk.h 。为方便起见,我们还将其包含在此处:

#ifndef __CAPSULETHUNK_H

#define __CAPSULETHUNK_H

#if ( (PY_VERSION_HEX < 0x02070000) \

|| ((PY_VERSION_HEX >= 0x03000000) \

&& (PY_VERSION_HEX < 0x03010000)) )

#define __PyCapsule_GetField(capsule, field, default_value) \

( PyCapsule_CheckExact(capsule) \

? (((PyCObject *)capsule)->field) \

: (default_value) \

) \

#define __PyCapsule_SetField(capsule, field, value) \

( PyCapsule_CheckExact(capsule) \

? (((PyCObject *)capsule)->field = value), 1 \

: 0 \

) \

#define PyCapsule_Type PyCObject_Type

#define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))

#define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))

#define PyCapsule_New(pointer, name, destructor) \

(PyCObject_FromVoidPtr(pointer, destructor))

#define PyCapsule_GetPointer(capsule, name) \

(PyCObject_AsVoidPtr(capsule))

/* Don't call PyCObject_SetPointer here, it fails if there's a destructor */

#define PyCapsule_SetPointer(capsule, pointer) \

__PyCapsule_SetField(capsule, cobject, pointer)

#define PyCapsule_GetDestructor(capsule) \

__PyCapsule_GetField(capsule, destructor)

#define PyCapsule_SetDestructor(capsule, dtor) \

__PyCapsule_SetField(capsule, destructor, dtor)

/*

* Sorry, there's simply no place

* to store a Capsule "name" in a CObject.

*/

#define PyCapsule_GetName(capsule) NULL

staticint

PyCapsule_SetName(PyObject*capsule,constchar*unused)

{

unused=unused;

PyErr_SetString(PyExc_NotImplementedError,

"can't use PyCapsule_SetName with CObjects");

return1;

}

#define PyCapsule_GetContext(capsule) \

__PyCapsule_GetField(capsule, descr)

#define PyCapsule_SetContext(capsule, context) \

__PyCapsule_SetField(capsule, descr, context)

staticvoid*

PyCapsule_Import(constchar*name,intno_block)

{

PyObject*object=NULL;

void*return_value=NULL;

char*trace;

size_tname_length=(strlen(name)+1)*sizeof(char);

char*name_dup=(char*)PyMem_MALLOC(name_length);

if(!name_dup){

returnNULL;

}

memcpy(name_dup,name,name_length);

trace=name_dup;

while(trace){

char*dot=strchr(trace,'.');

if(dot){

*dot++='\0';

}

if(object==NULL){

if(no_block){

object=PyImport_ImportModuleNoBlock(trace);

}else{

object=PyImport_ImportModule(trace);

if(!object){

PyErr_Format(PyExc_ImportError,

"PyCapsule_Import could not "

"import module \"%s\"",trace);

}

}

}else{

PyObject*object2=PyObject_GetAttrString(object,trace);

Py_DECREF(object);

object=object2;

}

if(!object){

gotoEXIT;

}

trace=dot;

}

if(PyCObject_Check(object)){

PyCObject*cobject=(PyCObject*)object;

return_value=cobject->cobject;

}else{

PyErr_Format(PyExc_AttributeError,

"PyCapsule_Import \"%s\" is not valid",

name);

}

EXIT:

Py_XDECREF(object);

if(name_dup){

PyMem_FREE(name_dup);

}

returnreturn_value;

}

#endif /* #if PY_VERSION_HEX < 0x02070000 */

#endif /* __CAPSULETHUNK_H */

其他选项¶

如果你正在编写新的扩展模块,你可能会考虑 Cython 。 它将类似 Python 的语言转换为 C 。它创建的扩展模块与 Python 3 和 Python 2 兼容。

以上是 Python将扩展模块移植到Python3 的全部内容, 来源链接: utcz.com/z/508082.html

回到顶部