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

cporting.md

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

1> **Источник:** https://python-all.ru/3.6/howto/cporting.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Перенос модулей расширения на Python 389**автор**1011Benjamin Peterson1213Аннотация1415Хотя изменение C-API не было одной из целей Python 3, многочисленные изменения на уровне Python сделали невозможным сохранение API Python 2 нетронутым. На самом деле, некоторые изменения, такие как унификация [`int()`](https://python-all.ru/3.6/library/functions.html#int) и `long()`, более очевидны на уровне C. Этот документ стремится описать несовместимости и способы их обхода.1617## Условная компиляция1819Самый простой способ скомпилировать только часть кода для Python 3 – проверить, что `PY_MAJOR_VERSION` больше или равно 3.2021```c22#if PY_MAJOR_VERSION >= 323#define IS_PY3K24#endif25```2627Отсутствующие функции API можно заменить псевдонимами на их эквиваленты в условных блоках.2829## Изменения в API объектов3031Python 3 объединил некоторые типы со схожими функциями, чётко разделив другие.3233### Унификация str/unicode3435Тип [`str()`](https://python-all.ru/3.6/library/stdtypes.html#str) в Python 3 эквивалентен типу `unicode()` в Python 2; для обоих используются C-функции `PyUnicode_*`. Старый 8-битный строковый тип стал [`bytes()`](https://python-all.ru/3.6/library/stdtypes.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`.3637```c38#include "stdlib.h"39#include "Python.h"40#include "bytesobject.h"4142/* пример текста */43static PyObject *44say_hello(PyObject *self, PyObject *args) {45    PyObject *name, *result;4647    if (!PyArg_ParseTuple(args, "U:say_hello", &name))48        return NULL;4950    result = PyUnicode_FromFormat("Hello, %S!", name);51    return result;52}5354/* просто пересылка */55static char * do_encode(PyObject *);5657/* пример с байтами */58static PyObject *59encode_object(PyObject *self, PyObject *args) {60    char *encoded;61    PyObject *result, *myobj;6263    if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))64        return NULL;6566    encoded = do_encode(myobj);67    if (encoded == NULL)68        return NULL;69    result = PyBytes_FromString(encoded);70    free(encoded);71    return result;72}73```7475### Унификация long/int7677В Python 3 есть только один целочисленный тип, [`int()`](https://python-all.ru/3.6/library/functions.html#int). Но на самом деле он соответствует типу `long()` из Python 2 – тип [`int()`](https://python-all.ru/3.6/library/functions.html#int), использовавшийся в Python 2, был удалён. В C-API функции `PyInt_*` заменены их эквивалентами `PyLong_*`.7879## Инициализация и состояние модуля8081В Python 3 переработана система инициализации модулей расширения. (См. [**PEP 3121**](https://python-all.ru/3.6/howto/cporting.html).) Вместо хранения состояния модуля в глобальных переменных его следует хранить в структуре, специфичной для интерпретатора. Создание модулей, корректно работающих как в Python 2, так и в Python 3, нетривиально. Следующий простой пример показывает, как это сделать.8283```c84#include "Python.h"8586struct module_state {87    PyObject *error;88};8990#if PY_MAJOR_VERSION >= 391#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))92#else93#define GETSTATE(m) (&_state)94static struct module_state _state;95#endif9697static PyObject *98error_out(PyObject *m) {99    struct module_state *st = GETSTATE(m);100    PyErr_SetString(st->error, "something bad happened");101    return NULL;102}103104static PyMethodDef myextension_methods[] = {105    {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},106    {NULL, NULL}107};108109#if PY_MAJOR_VERSION >= 3110111static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {112    Py_VISIT(GETSTATE(m)->error);113    return 0;114}115116static int myextension_clear(PyObject *m) {117    Py_CLEAR(GETSTATE(m)->error);118    return 0;119}120121static struct PyModuleDef moduledef = {122        PyModuleDef_HEAD_INIT,123        "myextension",124        NULL,125        sizeof(struct module_state),126        myextension_methods,127        NULL,128        myextension_traverse,129        myextension_clear,130        NULL131};132133#define INITERROR return NULL134135PyMODINIT_FUNC136PyInit_myextension(void)137138#else139#define INITERROR return140141void142initmyextension(void)143#endif144{145#if PY_MAJOR_VERSION >= 3146    PyObject *module = PyModule_Create(&moduledef);147#else148    PyObject *module = Py_InitModule("myextension", myextension_methods);149#endif150151    if (module == NULL)152        INITERROR;153    struct module_state *st = GETSTATE(module);154155    st->error = PyErr_NewException("myextension.Error", NULL, NULL);156    if (st->error == NULL) {157        Py_DECREF(module);158        INITERROR;159    }160161#if PY_MAJOR_VERSION >= 3162    return module;163#endif164}165```166167## CObject заменён на Capsule168169Объект `Capsule` был представлен в Python 3.1 и 2.7 для замены `CObject`. Объекты CObject были полезны, но API `CObject` был проблематичным: он не позволял различать корректные CObject, что при несоответствии CObject приводило к аварийному завершению интерпретатора, а некоторые его API полагались на неопределённое поведение в C. (Для дополнительного чтения о причинах появления капсул см. [bpo-5630](https://python-all.ru/3.6/howto/cporting.html).)170171Если вы в настоящее время используете 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 больше не поддерживается и не рекомендуется для использования в производственной среде.)172173Следующий пример заголовочного файла `capsulethunk.h` может решить проблему. Просто пишите свой код, используя API `Capsule`, и включайте этот заголовочный файл после `Python.h`. Ваш код будет автоматически использовать капсулы в версиях Python, где они есть, и переключаться на CObject, когда капсулы недоступны.174175`capsulethunk.h` имитирует капсулы с помощью CObject. Однако, `CObject` не предоставляет места для хранения «имени» капсулы. В результате имитируемые объекты `Capsule`, создаваемые `capsulethunk.h`, ведут себя немного иначе, чем настоящие капсулы. В частности:176177> - Параметр name, передаваемый в [`PyCapsule_New()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_New), игнорируется.178> - Параметр name, передаваемый в [`PyCapsule_IsValid()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_IsValid) и [`PyCapsule_GetPointer()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_GetPointer), игнорируется, и проверка имени на ошибки не выполняется.179> - [`PyCapsule_GetName()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_GetName) всегда возвращает NULL.180> - [`PyCapsule_SetName()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_SetName) всегда возбуждает исключение и возвращает признак ошибки. (Поскольку нет способа сохранить имя в CObject, было решено, что явное сообщение об ошибке при [`PyCapsule_SetName()`](https://python-all.ru/3.6/c-api/capsule.html#c.PyCapsule_SetName) здесь предпочтительнее молчаливого отказа. Если это неудобно, вы можете изменить свою локальную копию по своему усмотрению.)181182Вы можете найти `capsulethunk.h` в дистрибутиве исходных кодов Python как [Doc/includes/capsulethunk.h](https://python-all.ru/src/3.6/Doc/includes/capsulethunk.h). Мы также включили его сюда для вашего удобства:183184```c185#ifndef __CAPSULETHUNK_H186#define __CAPSULETHUNK_H187188#if (    (PY_VERSION_HEX <  0x02070000) \189     || ((PY_VERSION_HEX >= 0x03000000) \190      && (PY_VERSION_HEX <  0x03010000)) )191192#define __PyCapsule_GetField(capsule, field, default_value) \193    ( PyCapsule_CheckExact(capsule) \194        ? (((PyCObject *)capsule)->field) \195        : (default_value) \196    ) \197198#define __PyCapsule_SetField(capsule, field, value) \199    ( PyCapsule_CheckExact(capsule) \200        ? (((PyCObject *)capsule)->field = value), 1 \201        : 0 \202    ) \203204#define PyCapsule_Type PyCObject_Type205206#define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))207#define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))208209#define PyCapsule_New(pointer, name, destructor) \210    (PyCObject_FromVoidPtr(pointer, destructor))211212#define PyCapsule_GetPointer(capsule, name) \213    (PyCObject_AsVoidPtr(capsule))214215/* Не вызывайте PyCObject_SetPointer здесь, он завершится ошибкой, если есть деструктор */216#define PyCapsule_SetPointer(capsule, pointer) \217    __PyCapsule_SetField(capsule, cobject, pointer)218219#define PyCapsule_GetDestructor(capsule) \220    __PyCapsule_GetField(capsule, destructor)221222#define PyCapsule_SetDestructor(capsule, dtor) \223    __PyCapsule_SetField(capsule, destructor, dtor)224225/*226 * Извините, места просто нет227 * для хранения имени "name" Capsule в CObject.228 */229#define PyCapsule_GetName(capsule) NULL230231static int232PyCapsule_SetName(PyObject *capsule, const char *unused)233{234    unused = unused;235    PyErr_SetString(PyExc_NotImplementedError,236        "can't use PyCapsule_SetName with CObjects");237    return 1;238}239240#define PyCapsule_GetContext(capsule) \241    __PyCapsule_GetField(capsule, descr)242243#define PyCapsule_SetContext(capsule, context) \244    __PyCapsule_SetField(capsule, descr, context)245246static void *247PyCapsule_Import(const char *name, int no_block)248{249    PyObject *object = NULL;250    void *return_value = NULL;251    char *trace;252    size_t name_length = (strlen(name) + 1) * sizeof(char);253    char *name_dup = (char *)PyMem_MALLOC(name_length);254255    if (!name_dup) {256        return NULL;257    }258259    memcpy(name_dup, name, name_length);260261    trace = name_dup;262    while (trace) {263        char *dot = strchr(trace, '.');264        if (dot) {265            *dot++ = '\0';266        }267268        if (object == NULL) {269            if (no_block) {270                object = PyImport_ImportModuleNoBlock(trace);271            } else {272                object = PyImport_ImportModule(trace);273                if (!object) {274                    PyErr_Format(PyExc_ImportError,275                        "PyCapsule_Import could not "276                        "import module \"%s\"", trace);277                }278            }279        } else {280            PyObject *object2 = PyObject_GetAttrString(object, trace);281            Py_DECREF(object);282            object = object2;283        }284        if (!object) {285            goto EXIT;286        }287288        trace = dot;289    }290291    if (PyCObject_Check(object)) {292        PyCObject *cobject = (PyCObject *)object;293        return_value = cobject->cobject;294    } else {295        PyErr_Format(PyExc_AttributeError,296            "PyCapsule_Import \"%s\" is not valid",297            name);298    }299300EXIT:301    Py_XDECREF(object);302    if (name_dup) {303        PyMem_FREE(name_dup);304    }305    return return_value;306}307308#endif /* #if PY_VERSION_HEX < 0x02070000 */309310#endif /* __CAPSULETHUNK_H */311```312313## Другие варианты314315Если вы пишете новый модуль расширения, рассмотрите [Cython](https://python-all.ru/3.6/howto/cporting.html). Он транслирует язык, похожий на Python, в C. Создаваемые им модули расширения совместимы с Python 3 и Python 2.316