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

cporting.md

315 строк · 14.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.5/howto/cporting.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Перенос модулей расширения на Python 389| автор: | Benjamin Peterson |10| --- | --- |1112Аннотация1314Хотя изменение C-API не было одной из целей Python 3, многочисленные изменения на уровне Python сделали невозможным сохранение API Python 2 нетронутым. На самом деле, некоторые изменения, такие как унификация [`int()`](https://python-all.ru/3.5/library/functions.html#int) и `long()`, более очевидны на уровне C. Этот документ стремится описать несовместимости и способы их обхода.1516## Условная компиляция1718Самый простой способ скомпилировать только часть кода для Python 3 – проверить, что `PY_MAJOR_VERSION` больше или равно 3.1920```c21#if PY_MAJOR_VERSION >= 322#define IS_PY3K23#endif24```2526Отсутствующие функции API можно заменить псевдонимами на их эквиваленты в условных блоках.2728## Изменения в API объектов2930Python 3 объединил некоторые типы со схожими функциями, чётко разделив другие.3132### Унификация str/unicode3334Тип [`str()`](https://python-all.ru/3.5/library/stdtypes.html#str) в Python 3 эквивалентен типу `unicode()` в Python 2; для обоих используются C-функции `PyUnicode_*`. Старый 8-битный строковый тип стал [`bytes()`](https://python-all.ru/3.5/library/functions.html#bytes) с C-функциями `PyBytes_*`. Python 2.6 и выше предоставляют заголовочный файл совместимости `bytesobject.h`, отображающий имена `PyBytes` на `PyString`. Для наилучшей совместимости с Python 3 `PyUnicode` следует использовать для текстовых данных, а `PyBytes` для двоичных. Также важно помнить, что `PyBytes` и `PyUnicode` в Python 3 не взаимозаменяемы, как `PyString` и `PyUnicode` в Python 2. Следующий пример демонстрирует лучшие практики в отношении `PyUnicode`, `PyString`, и `PyBytes`.3536```c37#include "stdlib.h"38#include "Python.h"39#include "bytesobject.h"4041/* пример текста */42static PyObject *43say_hello(PyObject *self, PyObject *args) {44    PyObject *name, *result;4546    if (!PyArg_ParseTuple(args, "U:say_hello", &name))47        return NULL;4849    result = PyUnicode_FromFormat("Hello, %S!", name);50    return result;51}5253/* просто пересылка */54static char * do_encode(PyObject *);5556/* пример с байтами */57static PyObject *58encode_object(PyObject *self, PyObject *args) {59    char *encoded;60    PyObject *result, *myobj;6162    if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))63        return NULL;6465    encoded = do_encode(myobj);66    if (encoded == NULL)67        return NULL;68    result = PyBytes_FromString(encoded);69    free(encoded);70    return result;71}72```7374### Унификация long/int7576В Python 3 есть только один целочисленный тип, [`int()`](https://python-all.ru/3.5/library/functions.html#int). Но на самом деле он соответствует типу `long()` из Python 2 – тип [`int()`](https://python-all.ru/3.5/library/functions.html#int), использовавшийся в Python 2, был удалён. В C-API функции `PyInt_*` заменены их эквивалентами `PyLong_*`.7778## Инициализация и состояние модуля7980В Python 3 переработана система инициализации модулей расширения. (См. [**PEP 3121**](https://python-all.ru/3.5/howto/cporting.html).) Вместо хранения состояния модуля в глобальных переменных его следует хранить в структуре, специфичной для интерпретатора. Создание модулей, корректно работающих как в Python 2, так и в Python 3, нетривиально. Следующий простой пример показывает, как это сделать.8182```c83#include "Python.h"8485struct module_state {86    PyObject *error;87};8889#if PY_MAJOR_VERSION >= 390#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))91#else92#define GETSTATE(m) (&_state)93static struct module_state _state;94#endif9596static PyObject *97error_out(PyObject *m) {98    struct module_state *st = GETSTATE(m);99    PyErr_SetString(st->error, "something bad happened");100    return NULL;101}102103static PyMethodDef myextension_methods[] = {104    {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},105    {NULL, NULL}106};107108#if PY_MAJOR_VERSION >= 3109110static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {111    Py_VISIT(GETSTATE(m)->error);112    return 0;113}114115static int myextension_clear(PyObject *m) {116    Py_CLEAR(GETSTATE(m)->error);117    return 0;118}119120static struct PyModuleDef moduledef = {121        PyModuleDef_HEAD_INIT,122        "myextension",123        NULL,124        sizeof(struct module_state),125        myextension_methods,126        NULL,127        myextension_traverse,128        myextension_clear,129        NULL130};131132#define INITERROR return NULL133134PyMODINIT_FUNC135PyInit_myextension(void)136137#else138#define INITERROR return139140void141initmyextension(void)142#endif143{144#if PY_MAJOR_VERSION >= 3145    PyObject *module = PyModule_Create(&moduledef);146#else147    PyObject *module = Py_InitModule("myextension", myextension_methods);148#endif149150    if (module == NULL)151        INITERROR;152    struct module_state *st = GETSTATE(module);153154    st->error = PyErr_NewException("myextension.Error", NULL, NULL);155    if (st->error == NULL) {156        Py_DECREF(module);157        INITERROR;158    }159160#if PY_MAJOR_VERSION >= 3161    return module;162#endif163}164```165166## CObject заменён на Capsule167168Объект `Capsule` был представлен в Python 3.1 и 2.7 для замены `CObject`. Объекты CObject были полезны, но API `CObject` был проблематичным: он не позволял различать корректные CObject, что при несоответствии CObject приводило к аварийному завершению интерпретатора, а некоторые его API полагались на неопределённое поведение в C. (Для дополнительного чтения о причинах появления капсул см. [bpo-5630](https://python-all.ru/3.5/howto/cporting.html).)169170Если вы в настоящее время используете CObject и хотите перейти на Python 3.1 или новее, вам нужно переключиться на капсулы. `CObject` был объявлен устаревшим в Python 3.1 и 2.7 и полностью удалён в Python 3.2. Если вы поддерживаете только Python 2.7 или Python 3.1 и выше, вы можете просто переключиться на `Capsule`. Если вам нужно поддерживать Python 3.0 или версии Python, предшествующие 2.7, вам придётся поддерживать и CObject, и капсулы. (Обратите внимание, что Python 3.0 больше не поддерживается и не рекомендуется для использования в производственной среде.)171172Следующий пример заголовочного файла `capsulethunk.h` может решить проблему. Просто пишите свой код, используя API `Capsule`, и включайте этот заголовочный файл после `Python.h`. Ваш код будет автоматически использовать капсулы в версиях Python, где они есть, и переключаться на CObject, когда капсулы недоступны.173174`capsulethunk.h` имитирует капсулы с помощью CObject. Однако, `CObject` не предоставляет места для хранения «имени» капсулы. В результате имитируемые объекты `Capsule`, создаваемые `capsulethunk.h`, ведут себя немного иначе, чем настоящие капсулы. В частности:175176> - Параметр name, передаваемый в [`PyCapsule_New()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_New), игнорируется.177> - Параметр name, передаваемый в [`PyCapsule_IsValid()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_IsValid) и [`PyCapsule_GetPointer()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_GetPointer), игнорируется, и проверка имени на ошибки не выполняется.178> - [`PyCapsule_GetName()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_GetName) всегда возвращает NULL.179> - [`PyCapsule_SetName()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_SetName) всегда возбуждает исключение и возвращает признак ошибки. (Поскольку нет способа сохранить имя в CObject, было решено, что явное сообщение об ошибке при [`PyCapsule_SetName()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_SetName) здесь предпочтительнее молчаливого отказа. Если это неудобно, вы можете изменить свою локальную копию по своему усмотрению.)180181Вы можете найти `capsulethunk.h` в дистрибутиве исходных кодов Python как [Doc/includes/capsulethunk.h](https://python-all.ru/src/3.5/Doc/includes/capsulethunk.h). Мы также включили его сюда для вашего удобства:182183```c184#ifndef __CAPSULETHUNK_H185#define __CAPSULETHUNK_H186187#if (    (PY_VERSION_HEX <  0x02070000) \188     || ((PY_VERSION_HEX >= 0x03000000) \189      && (PY_VERSION_HEX <  0x03010000)) )190191#define __PyCapsule_GetField(capsule, field, default_value) \192    ( PyCapsule_CheckExact(capsule) \193        ? (((PyCObject *)capsule)->field) \194        : (default_value) \195    ) \196197#define __PyCapsule_SetField(capsule, field, value) \198    ( PyCapsule_CheckExact(capsule) \199        ? (((PyCObject *)capsule)->field = value), 1 \200        : 0 \201    ) \202203#define PyCapsule_Type PyCObject_Type204205#define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))206#define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))207208#define PyCapsule_New(pointer, name, destructor) \209    (PyCObject_FromVoidPtr(pointer, destructor))210211#define PyCapsule_GetPointer(capsule, name) \212    (PyCObject_AsVoidPtr(capsule))213214/* Не вызывайте PyCObject_SetPointer здесь, он завершится ошибкой, если есть деструктор */215#define PyCapsule_SetPointer(capsule, pointer) \216    __PyCapsule_SetField(capsule, cobject, pointer)217218#define PyCapsule_GetDestructor(capsule) \219    __PyCapsule_GetField(capsule, destructor)220221#define PyCapsule_SetDestructor(capsule, dtor) \222    __PyCapsule_SetField(capsule, destructor, dtor)223224/*225 * Извините, места просто нет226 * для хранения имени "name" Capsule в CObject.227 */228#define PyCapsule_GetName(capsule) NULL229230static int231PyCapsule_SetName(PyObject *capsule, const char *unused)232{233    unused = unused;234    PyErr_SetString(PyExc_NotImplementedError,235        "can't use PyCapsule_SetName with CObjects");236    return 1;237}238239#define PyCapsule_GetContext(capsule) \240    __PyCapsule_GetField(capsule, descr)241242#define PyCapsule_SetContext(capsule, context) \243    __PyCapsule_SetField(capsule, descr, context)244245static void *246PyCapsule_Import(const char *name, int no_block)247{248    PyObject *object = NULL;249    void *return_value = NULL;250    char *trace;251    size_t name_length = (strlen(name) + 1) * sizeof(char);252    char *name_dup = (char *)PyMem_MALLOC(name_length);253254    if (!name_dup) {255        return NULL;256    }257258    memcpy(name_dup, name, name_length);259260    trace = name_dup;261    while (trace) {262        char *dot = strchr(trace, '.');263        if (dot) {264            *dot++ = '\0';265        }266267        if (object == NULL) {268            if (no_block) {269                object = PyImport_ImportModuleNoBlock(trace);270            } else {271                object = PyImport_ImportModule(trace);272                if (!object) {273                    PyErr_Format(PyExc_ImportError,274                        "PyCapsule_Import could not "275                        "import module \"%s\"", trace);276                }277            }278        } else {279            PyObject *object2 = PyObject_GetAttrString(object, trace);280            Py_DECREF(object);281            object = object2;282        }283        if (!object) {284            goto EXIT;285        }286287        trace = dot;288    }289290    if (PyCObject_Check(object)) {291        PyCObject *cobject = (PyCObject *)object;292        return_value = cobject->cobject;293    } else {294        PyErr_Format(PyExc_AttributeError,295            "PyCapsule_Import \"%s\" is not valid",296            name);297    }298299EXIT:300    Py_XDECREF(object);301    if (name_dup) {302        PyMem_FREE(name_dup);303    }304    return return_value;305}306307#endif /* #if PY_VERSION_HEX < 0x02070000 */308309#endif /* __CAPSULETHUNK_H */310```311312## Другие варианты313314Если вы пишете новый модуль расширения, рассмотрите [Cython](https://python-all.ru/3.5/howto/cporting.html). Он транслирует язык, похожий на Python, в C. Создаваемые им модули расширения совместимы с Python 3 и Python 2.315