将扩展模块移植到 Python 3

作者

Benjamin Peterson

摘要

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

条件编译

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

  1. #if PY_MAJOR_VERSION >= 3
  2. #define IS_PY3K
  3. #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 。:

  1. #include "stdlib.h"
  2. #include "Python.h"
  3. #include "bytesobject.h"
  4. /* text example */
  5. static PyObject *
  6. say_hello(PyObject *self, PyObject *args) {
  7. PyObject *name, *result;
  8. if (!PyArg_ParseTuple(args, "U:say_hello", &name))
  9. return NULL;
  10. result = PyUnicode_FromFormat("Hello, %S!", name);
  11. return result;
  12. }
  13. /* just a forward */
  14. static char * do_encode(PyObject *);
  15. /* bytes example */
  16. static PyObject *
  17. encode_object(PyObject *self, PyObject *args) {
  18. char *encoded;
  19. PyObject *result, *myobj;
  20. if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))
  21. return NULL;
  22. encoded = do_encode(myobj);
  23. if (encoded == NULL)
  24. return NULL;
  25. result = PyBytes_FromString(encoded);
  26. free(encoded);
  27. return result;
  28. }

long/int 统一

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

模块初始化和状态

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

  1. #include "Python.h"
  2. struct module_state {
  3. PyObject *error;
  4. };
  5. #if PY_MAJOR_VERSION >= 3
  6. #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
  7. #else
  8. #define GETSTATE(m) (&_state)
  9. static struct module_state _state;
  10. #endif
  11. static PyObject *
  12. error_out(PyObject *m) {
  13. struct module_state *st = GETSTATE(m);
  14. PyErr_SetString(st->error, "something bad happened");
  15. return NULL;
  16. }
  17. static PyMethodDef myextension_methods[] = {
  18. {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
  19. {NULL, NULL}
  20. };
  21. #if PY_MAJOR_VERSION >= 3
  22. static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {
  23. Py_VISIT(GETSTATE(m)->error);
  24. return 0;
  25. }
  26. static int myextension_clear(PyObject *m) {
  27. Py_CLEAR(GETSTATE(m)->error);
  28. return 0;
  29. }
  30. static struct PyModuleDef moduledef = {
  31. PyModuleDef_HEAD_INIT,
  32. "myextension",
  33. NULL,
  34. sizeof(struct module_state),
  35. myextension_methods,
  36. NULL,
  37. myextension_traverse,
  38. myextension_clear,
  39. NULL
  40. };
  41. #define INITERROR return NULL
  42. PyMODINIT_FUNC
  43. PyInit_myextension(void)
  44. #else
  45. #define INITERROR return
  46. void
  47. initmyextension(void)
  48. #endif
  49. {
  50. #if PY_MAJOR_VERSION >= 3
  51. PyObject *module = PyModule_Create(&moduledef);
  52. #else
  53. PyObject *module = Py_InitModule("myextension", myextension_methods);
  54. #endif
  55. if (module == NULL)
  56. INITERROR;
  57. struct module_state *st = GETSTATE(module);
  58. st->error = PyErr_NewException("myextension.Error", NULL, NULL);
  59. if (st->error == NULL) {
  60. Py_DECREF(module);
  61. INITERROR;
  62. }
  63. #if PY_MAJOR_VERSION >= 3
  64. return module;
  65. #endif
  66. }

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 略有不同。特别地:

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

  1. #ifndef __CAPSULETHUNK_H
  2. #define __CAPSULETHUNK_H
  3. #if ( (PY_VERSION_HEX < 0x02070000) \
  4. || ((PY_VERSION_HEX >= 0x03000000) \
  5. && (PY_VERSION_HEX < 0x03010000)) )
  6. #define __PyCapsule_GetField(capsule, field, default_value) \
  7. ( PyCapsule_CheckExact(capsule) \
  8. ? (((PyCObject *)capsule)->field) \
  9. : (default_value) \
  10. ) \
  11. #define __PyCapsule_SetField(capsule, field, value) \
  12. ( PyCapsule_CheckExact(capsule) \
  13. ? (((PyCObject *)capsule)->field = value), 1 \
  14. : 0 \
  15. ) \
  16. #define PyCapsule_Type PyCObject_Type
  17. #define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
  18. #define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))
  19. #define PyCapsule_New(pointer, name, destructor) \
  20. (PyCObject_FromVoidPtr(pointer, destructor))
  21. #define PyCapsule_GetPointer(capsule, name) \
  22. (PyCObject_AsVoidPtr(capsule))
  23. /* Don't call PyCObject_SetPointer here, it fails if there's a destructor */
  24. #define PyCapsule_SetPointer(capsule, pointer) \
  25. __PyCapsule_SetField(capsule, cobject, pointer)
  26. #define PyCapsule_GetDestructor(capsule) \
  27. __PyCapsule_GetField(capsule, destructor)
  28. #define PyCapsule_SetDestructor(capsule, dtor) \
  29. __PyCapsule_SetField(capsule, destructor, dtor)
  30. /*
  31. * Sorry, there's simply no place
  32. * to store a Capsule "name" in a CObject.
  33. */
  34. #define PyCapsule_GetName(capsule) NULL
  35. static int
  36. PyCapsule_SetName(PyObject *capsule, const char *unused)
  37. {
  38. unused = unused;
  39. PyErr_SetString(PyExc_NotImplementedError,
  40. "can't use PyCapsule_SetName with CObjects");
  41. return 1;
  42. }
  43. #define PyCapsule_GetContext(capsule) \
  44. __PyCapsule_GetField(capsule, descr)
  45. #define PyCapsule_SetContext(capsule, context) \
  46. __PyCapsule_SetField(capsule, descr, context)
  47. static void *
  48. PyCapsule_Import(const char *name, int no_block)
  49. {
  50. PyObject *object = NULL;
  51. void *return_value = NULL;
  52. char *trace;
  53. size_t name_length = (strlen(name) + 1) * sizeof(char);
  54. char *name_dup = (char *)PyMem_MALLOC(name_length);
  55. if (!name_dup) {
  56. return NULL;
  57. }
  58. memcpy(name_dup, name, name_length);
  59. trace = name_dup;
  60. while (trace) {
  61. char *dot = strchr(trace, '.');
  62. if (dot) {
  63. *dot++ = '\0';
  64. }
  65. if (object == NULL) {
  66. if (no_block) {
  67. object = PyImport_ImportModuleNoBlock(trace);
  68. } else {
  69. object = PyImport_ImportModule(trace);
  70. if (!object) {
  71. PyErr_Format(PyExc_ImportError,
  72. "PyCapsule_Import could not "
  73. "import module \"%s\"", trace);
  74. }
  75. }
  76. } else {
  77. PyObject *object2 = PyObject_GetAttrString(object, trace);
  78. Py_DECREF(object);
  79. object = object2;
  80. }
  81. if (!object) {
  82. goto EXIT;
  83. }
  84. trace = dot;
  85. }
  86. if (PyCObject_Check(object)) {
  87. PyCObject *cobject = (PyCObject *)object;
  88. return_value = cobject->cobject;
  89. } else {
  90. PyErr_Format(PyExc_AttributeError,
  91. "PyCapsule_Import \"%s\" is not valid",
  92. name);
  93. }
  94. EXIT:
  95. Py_XDECREF(object);
  96. if (name_dup) {
  97. PyMem_FREE(name_dup);
  98. }
  99. return return_value;
  100. }
  101. #endif /* #if PY_VERSION_HEX < 0x02070000 */
  102. #endif /* __CAPSULETHUNK_H */

其他选项

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