Документация Python неофициальный перевод
Содержание страницы

Портирование модулей расширения на 3.0Porting Extension Modules to 3.0

автор:Benjamin Peterson

Аннотация

Хотя изменение C-API не входило в цели Python 3.0, многочисленные изменения на уровне Python сделали невозможным сохранение API версии 2.x нетронутым. Фактически, некоторые изменения, такие как унификация int() и long(), более очевидны на уровне C. Данный документ стремится описать несовместимости и способы их обхода.

Условная компиляцияConditional compilation

Самый простой способ компилировать только часть кода для 3.0 – проверить, что PY_MAJOR_VERSION больше или равно 3.

#if PY_MAJOR_VERSION >= 3
#define IS_PY3K
#endif

Отсутствующие функции API можно заменить псевдонимами на их эквиваленты в условных блоках.

Изменения в API объектовChanges to Object APIs

Python 3.0 объединил некоторые типы со схожими функциями, чётко разделив другие.

Унификация str/unicodestr/unicode Unification

Тип str() (функции PyString_* в C) в Python 3.0 эквивалентен unicode() в 2.x (PyUnicode_*). Старый 8-битный строковый тип стал bytes(). Начиная с Python 2.6 предоставляется совместимый заголовочный файл bytesobject.h, который отображает имена PyBytes на PyString. Для наилучшей совместимости с 3.0 PyUnicode следует использовать для текстовых данных, а PyBytes – для двоичных. Также важно помнить, что PyBytes и PyUnicode в 3.0 не взаимозаменяемы, как PyString и PyUnicode в 2.x. Следующий пример демонстрирует наилучшие практики в отношении PyUnicode, PyString и PyBytes.

#include "stdlib.h"
#include "Python.h"
#include "bytesobject.h"

/* пример текста */
static PyObject *
say_hello(PyObject *self, PyObject *args) {
    PyObject *name, *result;

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

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

/* просто пересылка */
static char * do_encode(PyObject *);

/* пример с байтами */
static PyObject *
encode_object(PyObject *self, PyObject *args) {
    char *encoded;
    PyObject *result, *myobj;

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

    encoded = do_encode(myobj);
    if (encoded == NULL)
        return NULL;
    result = PyBytes_FromString(encoded);
    free(encoded);
    return result;
}

Унификация long/intlong/int Unification

В Python 3.0 существует только один целочисленный тип. На уровне Python он называется int(), но на самом деле соответствует типу long() из 2.x. В C-API функции PyInt_* заменены их аналогами PyLong_*. Лучший подход – использовать функции PyInt_*, которые являются псевдонимами PyLong_*, определёнными в intobject.h. Абстрактные API PyNumber_* также могут быть использованы в некоторых случаях.

#include "Python.h"
#include "intobject.h"

static PyObject *
add_ints(PyObject *self, PyObject *args) {
    int one, two;
    PyObject *result;

    if (!PyArg_ParseTuple(args, "ii:add_ints", &one, &two))
        return NULL;

    return PyInt_FromLong(one + two);
}

Инициализация и состояние модуляModule initialization and state

Python 3.0 имеет переработанную систему инициализации модулей расширения. (См. PEP PEP 3121.) Вместо хранения состояния модуля в глобальных переменных, его следует хранить в структуре, специфичной для интерпретатора. Создание модулей, правильно работающих как в 2.x, так и в 3.0, – непростая задача. Следующий простой пример демонстрирует, как это сделать.

#include "Python.h"

struct module_state {
    PyObject *error;
};

#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
static struct module_state _state;
#endif

static PyObject *
error_out(PyObject *m) {
    struct module_state *st = GETSTATE(m);
    PyErr_SetString(st->error, "something bad happened");
    return NULL;
}

static PyMethodDef myextension_methods[] = {
    {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
    {NULL, NULL}
};

#if PY_MAJOR_VERSION >= 3

static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {
    Py_VISIT(GETSTATE(m)->error);
    return 0;
}

static int myextension_clear(PyObject *m) {
    Py_CLEAR(GETSTATE(m)->error);
    return 0;
}


static struct PyModuleDef moduledef = {
        PyModuleDef_HEAD_INIT,
        "myextension",
        NULL,
        sizeof(struct module_state),
        myextension_methods,
        NULL,
        myextension_traverse,
        myextension_clear,
        NULL
};

#define INITERROR return NULL

PyObject *
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;
    struct module_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
    return module;
#endif
}

Другие вариантыOther options

Если вы пишете новый модуль расширения, возможно, стоит рассмотреть Cython. Он транслирует язык, похожий на Python, в C. Создаваемые им модули расширения совместимы с Python 3.x и 2.x.