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

abi3t-migration.md

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

1> **Источник:** https://python-all.ru/3.16/howto/abi3t-migration.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Миграция на стабильный ABI для свободной многопоточности (`abi3t`)89Начиная с версии 3.15, CPython поддерживает вариант стабильного ABI, который поддерживает [со свободной многопоточностью](https://python-all.ru/3.16/glossary.html#term-free-threading) Python: стабильный ABI для сборок со свободной многопоточностью, или сокращённо `abi3t`. В этом документе описывается, как адаптировать расширения C API для поддержки свободной многопоточности.1011## Зачем это нужно1213Обычная причина использования стабильного ABI – сократить количество артефактов, которые необходимо собирать и распространять для каждой версии своей библиотеки.1415Без стабильного ABI необходимо собирать отдельную разделяемую библиотеку и, как правило, дистрибутив *wheel* для каждой функциональной версии CPython, которую требуется поддерживать. Например, каждый тег в следующей таблице представляет отдельную библиотеку/wheel:1617| Версия CPython | Без свободной многопоточности | Со свободной многопоточностью |18| --- | --- | --- |19| 3.12 | `cpython-312` | – |20| 3.13 | `cpython-313` | `cpython-313t` |21| 3.14 | `cpython-314` | `cpython-314t` |22| 3.15 | `cpython-315` | `cpython-315t` |23| 3.16 | `cpython-316` | `cpython-316t` |24| Более поздние версии | `cpython-3XX` | `cpython-3XXt` |2526Это много сборок, особенно если умножить на количество поддерживаемых платформ.2728Благодаря стабильному ABI (`abi3`, представленному в CPython 3.2), одно расширение (на платформу) может покрывать все сборки *без свободной потоковости* CPython:2930| Версия CPython | Без свободной потоковости | Со свободной потоковостью |31| --- | --- | --- |32| 3.12 | `abi3` | – |33| 3.13 | `cpython-313t` |  |34| 3.14 | `cpython-314t` |  |35| 3.15 | `cpython-315t` |  |36| 3.16 | `cpython-316t` |  |37| Более поздние версии | `cpython-3XXt` |  |3839Стабильный ABI для сборок со свободной потоковостью (`abi3t`), представленный в CPython 3.15, делает то же самое для сборок со свободной потоковостью. И он также совместим со сборками без свободной потоковости:4041| Версия CPython | Без свободной потоковости | Со свободной потоковостью |42| --- | --- | --- |43| 3.12 | `abi3` \* | – |44| 3.13 | `cpython-313t` |  |45| 3.14 | `cpython-314t` |  |46| 3.15 | `abi3t` |  |47| 3.16 |  |  |48| Более поздние версии |  |  |4950\* (Как и выше, расширение `abi3` совместимо со всеми сборками без свободной потоковости; даже с теми версиями 3.15+, которые эта таблица «приписывает» `abi3t`.)5152### Почему бы *не* сделать это5354У стабильного ABI есть два основных недостатка.5556Во-первых, ваше расширение может работать медленнее, так как стабильный ABI ставит совместимость выше производительности. Разница обычно незаметна, и её часто можно смягчить, используя один и тот же исходный код для сборки как со стабильным ABI, так и нескольких версионно-специфичных сборок для «основных» версий CPython.5758Во-вторых, доступна не вся часть C API. Расширения необходимо портировать для сборки со стабильным ABI, что может быть сложно или, в редких случаях, невозможно.5960В частности, `abi3t` требует API, добавленных в CPython 3.15. Если вы хотите собирать расширение для более старых версий CPython из одного исходного кода, у вас есть два основных варианта:6162- Используйте условные конструкции препроцессора.6364  При следовании этому руководству используйте блоки `#ifdef Py_TARGET_ABI3T` всякий раз, когда вам говорят внести изменение, которое нарушает сборку на интересующих вас версиях CPython. Оставляйте существующий код в блоках `#else`.6566  Для написанных вручную расширений C такой подход разумен вплоть до CPython 3.12, благодаря дополнениям, введённым в [**PEP 697**](https://python-all.ru/3.16/howto/abi3t-migration.html). Сохранение совместимости с 3.11 и ниже может быть оправдано для генераторов кода (например, Cython).67- Не портируйте на `abi3t` и продолжайте собирать отдельные расширения для каждой версии CPython, пока не сможете отказаться от поддержки старых версий.6869  Это допустимый подход. Не всем расширениям нужно переходить на `abi3t` прямо сейчас.7071## Предварительные требования7273В этом руководстве предполагается, что у вас есть расширение, написанное непосредственно на C (или C++), которое вы хотите портировать на `abi3t`.7475Если ваше расширение использует генератор кода (например, Cython) или языковую привязку (например, PyO3), лучше подождать, пока этот инструмент не получит поддержку `abi3t`. Если вы поддерживаете такой инструмент, возможно, вы сможете адаптировать инструкции из этого руководства для своего инструмента.7677### Стабильный ABI без свободной потоковости7879Ваше расширение должно поддерживать стабильный ABI (`abi3t`). Если нет, либо сначала портируйте его, либо следуйте этому руководству, но будьте готовы исправить проблемы, которые в нём не упомянуты.8081### Поддержка свободной потоковости8283Хотя технически это не строгое требование, скорее всего, вы захотите подготовить расширение для свободной потоковости, прежде чем портировать его на `abi3t`. См. [Поддержка расширений C API для свободной потоковости](https://python-all.ru/3.16/howto/free-threading-extensions.html#freethreading-extensions-howto) за инструкциями.8485> **См. также**86>87> [Портирование модулей расширений для поддержки свободной потоковости](https://python-all.ru/3.16/howto/abi3t-migration.html): Руководство по портированию, поддерживаемое сообществом, для авторов расширений.8889### Изоляция модулей расширений9091Ваш модуль должен использовать [многофазную инициализацию](https://python-all.ru/3.16/c-api/extension-modules.html#multi-phase-initialization), и должен быть либо изолированным, либо ограничиваться загрузкой не более одного раза на процесс. Если это не ваш случай, сначала следуйте [разделу «Изоляция расширений модулей»](https://python-all.ru/3.16/howto/isolating-extensions.html#isolating-extensions-howto). (См. [раздел об отказе](https://python-all.ru/3.16/howto/isolating-extensions.html#isolating-extensions-optout) для быстрого решения.)9293### Избегание типов переменного размера9495Если ваше расширение определяет типы переменного размера (с помощью [`Py_tp_itemsize`](https://python-all.ru/3.16/c-api/type.html#c.Py_tp_itemsize) или [`PyTypeObject.tp_itemsize`](https://python-all.ru/3.16/c-api/typeobj.html#c.PyTypeObject.tp_itemsize)), его невозможно портировать на `abi3t` 3.15.9697## Настройка сборки9899Если вы используете инструмент сборки (например, setuptools, meson-python, scikit-build-core), найдите в его документации способ выбрать `abi3t`. На момент написания не все они это поддерживают; но если ваш инструмент поддерживает, используйте его. Возможно, вам стоит проверить, что он установил правильный флаг, временно добавив следующий код сразу после `#include <Python.h>`:100101```c102#if Py_TARGET_ABI3T+0 <= 0x30f0000103#error "abi3t define is not set!"104#endif105```106107Это должно привести к другой ошибке, нежели «`abt3t` define is not set».108109> **Примечание**110>111> Если ваш инструмент сборки ещё не поддерживает `abi3t`, установите следующий макрос перед включением `Python.h`:112>113> ```c114> #define Py_TARGET_ABI3T 0x30f0000115> ```116>117> или укажите его как флаг компилятора, например:118>119> ```c120> -DPy_TARGET_ABI3T=0x30f0000121> ```122>123> Как только ваше расширение будет собираться с этой настройкой, оно станет совместимым с CPython 3.15 и выше.124>125> Если вы устанавливаете этот макрос вручную, вам также потребуется вручную назвать и пометить полученное расширение. Это описано в разделе [«Пометка и распространение»](https://python-all.ru/3.16/howto/abi3t-migration.html#abi3t-migration-tagging) ниже.126127В этом руководстве вам будет предложено выполнить ряд изменений. После каждого изменения проверяйте, что ваше расширение по-прежнему собирается в исходной (не-`abi3t`) конфигурации, и желательно запускайте тесты на всех поддерживаемых вами версиях Python. Это гарантирует, что в процессе портирования ничего не сломается.128129## Хук экспорта модуля130131Если вы ещё не выполнили этот шаг, ваш модуль расширения определяет [функцию инициализации модуля](https://python-all.ru/3.16/c-api/extension-modules.html#extension-pyinit) с именем `PyInit_<module_name>`. Вам нужно будет перенести её в [хук экспорта модуля](https://python-all.ru/3.16/c-api/extension-modules.html#extension-export-hook), `PyModExport_<module name>`, функцию, добавленную в CPython 3.15 в [**PEP 793**](https://python-all.ru/3.16/howto/abi3t-migration.html).132133Ваша существующая функция init должна выглядеть примерно так (с вашими собственными именами для `<modname>` и `<moddef>`):134135```c136PyMODINIT_FUNC137PyInit_<modname>(void)138{139    return PyModuleDef_Init(&<moddef>);140}141```142143Если перед `return` есть какой-то код, переместите его в слот-функцию [`Py_mod_create`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_create) или [`Py_mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec). См. [документацию PyInit](https://python-all.ru/3.16/c-api/extension-modules.html#extension-pyinit) для получения дополнительной информации.144145Функция ссылается на объект `PyModuleDef` (`<moddef>` в коде выше). Его определение должно быть похоже на следующее, с другими значениями и, возможно, некоторыми неименованными или пропущенными полями:146147```c148static PyModuleDef <moddef> = {149    PyModuleDef_HEAD_INIT,150    .m_name = "my_module",151    .m_doc = "my docstring",152    .m_size = sizeof(my_state_struct),153    .m_methods = my_methods,154    .m_slots = my_slots,155    .m_traverse = my_traverse,156    .m_clear = my_clear,157    .m_free = my_free,158};159```160161Удалите это определение и функцию `PyInit` (или поместите их в блок `#ifndef Py_TARGET_ABI3T` для сохранения обратной совместимости), и замените их следующим:162163```c164PyABIInfo_VAR(abi_info);165166static PySlot my_slot_array[] = {167    PySlot_STATIC_DATA(Py_mod_abi, &abi_info),168    PySlot_STATIC_DATA(Py_mod_name, "my_module"),169    PySlot_STATIC_DATA(Py_mod_doc, "my docstring"),170    PySlot_SIZE(Py_mod_state_size, sizeof(my_state_struct)),171    PySlot_STATIC_DATA(Py_mod_methods, my_methods),172    PySlot_STATIC_DATA(Py_mod_slots, my_slots),173    PySlot_FUNC(Py_mod_state_traverse, my_traverse),174    PySlot_FUNC(Py_mod_state_clear, my_clear),175    PySlot_FUNC(Py_mod_state_free, my_free),176    PySlot_END177};178179PyMODEXPORT_FUNC180PyModExport_<modname>(void)181{182    return my_slot_array;183}184```185186Опустите любые поля, которые отсутствовали (кроме нового [`Py_mod_abi`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_abi)), и подставьте свои собственные значения.187188См. документацию [`PySlot`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot) и [хука экспорта](https://python-all.ru/3.16/c-api/extension-modules.html#extension-export-hook) для получения подробностей об этом API.189190Как в примере, ваша функция `PyModExport_` должна *только* возвращать указатель на статические данные. Если вы не можете избежать дополнительного кода, обратитесь к [предостережениям в документации PyModExport](https://python-all.ru/3.16/c-api/extension-modules.html#pymodexport-api-caveats).191192### Существующие слоты193194Если у вас есть слот `Py_mod_slots`, проверьте массив, на который он ссылается. Это должен быть массив [`PyModuleDef_Slot`](https://python-all.ru/3.16/c-api/module.html#c.PyModuleDef_Slot), например:195196```c197static PyObject *create_module(PyObject *spec, PyModuleDef *def) { ... }198static int my_first_module_exec(PyObject *module) { ... }199static int my_second_module_exec(PyObject *module) { ... }200201static PyModuleDef_Slot my_slots[] = {202   {Py_mod_gil, Py_MOD_GIL_NOT_USED},203   {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},204   {Py_mod_create, my_module_create},205   {Py_mod_exec, my_first_module_exec},206   {Py_mod_exec, my_second_module_exec},207   {0, NULL}208};209```210211#### `py_mod_create`212213Если у вас есть запись [`Py_mod_create`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_create), убедитесь, что функцию можно вызвать с `NULL` в качестве второго аргумента (вместо [`PyModuleDef`](https://python-all.ru/3.16/c-api/module.html#c.PyModuleDef), который вы удаляете). Часто этот аргумент вообще не используется; вы можете проверить, переименовав его:214215```c216static PyObject *create_module(PyObject *spec, PyModuleDef *_unused) { ... }217```218219Если аргумент используется, найдите другой способ передачи данных. Обычно информация статична, и вы можете обращаться к ней напрямую. (Если вы используете одну функцию для нескольких разных модулей, рассмотрите возможность определить несколько функций вместо этого.)220221#### Несколько `py_mod_exec`222223Если у вас есть *более одной* записи [`Py_mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec), объедините их: создайте новую функцию, которая вызывает остальные, и замените ею существующие слоты.224225```c226static int my_module_exec(PyObject *module) {227   if (my_first_module_exec(module) < 0) return -1;228   if (my_second_module_exec(module) < 0) return -1;229}230231static PyModuleDef_Slot my_slots[] = {232   ...233   /* удалить остальные слоты Py_mod_exec */234   ...235   {Py_mod_exec, my_module_exec},236   {0, NULL}237};238```239240Если функции нигде больше не используются, вы можете вместо этого объединить их тела.241242#### Объединение массивов слотов243244Опционально, когда вы нарушаете совместимость с Python 3.14, вы можете очистить код, переместив слоты в массив [`PySlot`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot) и преобразовав определения в [`PySlot_DATA`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot_DATA) и [`PySlot_FUNC`](https://python-all.ru/3.16/c-api/slots.html#c.PySlot_FUNC):245246```c247static PySlot my_slot_array[] = {248    ...249    PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),250    PySlot_DATA(Py_mod_multiple_interpreters,251         Py_MOD_PER_INTERPRETER_GIL_SUPPORTED)252    PySlot_FUNC(Py_mod_create, my_module_create),253    PySlot_FUNC(Py_mod_exec, my_module_exec),254    PySlot_END255};256```257258Если вы это сделаете, удалите исходный массив [`PyModuleDef_Slot`](https://python-all.ru/3.16/c-api/module.html#c.PyModuleDef_Slot) и его запись `Py_mod_slots`.259260### Связанные `PyModuleDef`261262Поскольку новый API не использует структуру `PyModuleDef`, определение не будет связано с результирующим модулем. Это изменяет поведение следующих функций:263264- [`PyModule_GetDef()`](https://python-all.ru/3.16/c-api/module.html#c.PyModule_GetDef)265- [`PyType_GetModuleByDef()`](https://python-all.ru/3.16/c-api/type.html#c.PyType_GetModuleByDef)266267Проверьте свой код на наличие этих функций. Если вы их не используете, можете пропустить этот раздел.268269Обычно эти функции применяются для двух целей:2702711. Получить определение, с которым был создан модуль. В новом API это больше невозможно. Модули больше не хранят ссылку на определение, поэтому придётся найти другой способ передавать нужные данные.2722732. Проверить, «ваш» ли данный объект модуля. Теперь эту задачу решают [токены модулей](https://python-all.ru/3.16/c-api/module.html#ext-module-token) – непрозрачные указатели, идентифицирующие модуль. Чтобы использовать токен, объявите (или переиспользуйте) уникальную статическую переменную, например:274275   ```c276   static char my_token;277   ```278279   и добавьте указатель на неё в новую запись массива `PySlot` вашего модуля:280281   ```c282   static PySlot my_slot_array[] = {283      ...284      PySlot_STATIC_DATA(Py_mod_token, &my_token),285      PySlot_END286   }287   ```288289   Затем перейдите от вызовов [`PyModule_GetDef()`](https://python-all.ru/3.16/c-api/module.html#c.PyModule_GetDef) вида:290291   ```c292   PyModuleDef *def = PyModule_GetDef(module);293   ```294295   к [`PyModule_GetToken()`](https://python-all.ru/3.16/c-api/module.html#c.PyModule_GetToken) (которая использует выходной аргумент и может завершиться исключением):296297   ```c298   void *token;299   if (PyModule_GetToken(module, &token) < 0) {300       /* обработать ошибку */301   }302   ```303304   и от вызовов [`PyType_GetModuleByDef()`](https://python-all.ru/3.16/c-api/type.html#c.PyType_GetModuleByDef) вида:305306   ```c307   PyObject *module = PyType_GetModuleByDef(type, my_def);308   /* обработать ошибку; использовать модуль */309   ```310311   к [`PyType_GetModuleByToken()`](https://python-all.ru/3.16/c-api/type.html#c.PyType_GetModuleByToken) (которая возвращает сильную ссылку):312313   ```c314   PyObject *module = PyType_GetModuleByToken(type, my_token);315   /* обработать ошибку; использовать модуль */316   Py_XDECREF(module);317   ```318319## `PyObject` непрозрачность320321Структуры [`PyObject`](https://python-all.ru/3.16/c-api/structures.html#c.PyObject) и [`PyVarObject`](https://python-all.ru/3.16/c-api/structures.html#c.PyVarObject) являются непрозрачными в `abi3t`.322323Обращаться к их членам запрещено. Если вы это делали, переключитесь на функции-геттеры/сеттеры, упомянутые в их документации:324325- [`PyObject.ob_type`](https://python-all.ru/3.16/c-api/structures.html#c.PyObject.ob_type)326- [`PyObject.ob_refcnt`](https://python-all.ru/3.16/c-api/structures.html#c.PyObject.ob_refcnt)327- [`PyVarObject.ob_size`](https://python-all.ru/3.16/c-api/structures.html#c.PyVarObject.ob_size)328329Кроме того, компилятору неизвестен *размер* структур [`PyObject`](https://python-all.ru/3.16/c-api/structures.html#c.PyObject). Он может – и *действительно* – изменяться между разными сборками CPython.330331> **Примечание**332>333> Хотя размер доступен во время выполнения (например, как `sys.getsizeof(object())` в коде Python), не стоит поддаваться искушению вычислять по нему смещения указателей. Расположение полей объекта может измениться в будущих реализациях `abi3t`.334335### Определения пользовательских типов336337Поскольку `PyObject` непрозрачен, традиционный способ определения пользовательских типов больше не работает:338339```c340typedef struct {341   PyObject_HEAD  // раскрывается в `PyObject ob_base;` с неизвестным размером342343   int my_data;344} CustomObject;345346static PyType_Spec CustomType_spec = {347   ...348   .basicsize = sizeof(CustomObject),349   ...350};351```352353Скорее всего, все определения ваших классов *и* весь код, обращающийся к данным этих классов, нужно будет переписать. Вероятно, это самое большое изменение, которое потребуется для поддержки `abi3t`.354355Для каждого такого типа вместо определения `struct` для всего экземпляра определите его только с «дополнительными» полями – теми, что относятся к вашему классу, а не к его суперклассам:356357```c358typedef struct {359   int my_data;360} CustomObjectData;361```362363Измените имя. Почти весь код, использующий эту структуру, нужно будет изменить (в частности, указатели на новую структуру нельзя просто приводить к `PyObject*` и обратно), а смена имени позволит подсветить все места использования как ошибки компилятора. (Если вы используете `typeof`, C++ `auto` или подобные способы, чтобы не писать имя типа, это не сработает. Будьте особенно внимательны и рассмотрите запуск инструментов для поиска неопределённого поведения.)364365Затем для создания класса используйте *отрицательное* значение `basicsize`, чтобы указать «дополнительное» пространство для хранения, а не *общий* размер экземпляра:366367```c368static PyType_Spec CustomType_spec = {369   ...370   .basicsize = -sizeof(CustomObjectData), /* обратите внимание на знак минус */371   ...372};373```374375Если вы используете [`Py_tp_members`](https://python-all.ru/3.16/c-api/typeobj.html#c.Py_tp_members), установите флаг [`Py_RELATIVE_OFFSET`](https://python-all.ru/3.16/c-api/structures.html#c.Py_RELATIVE_OFFSET) для каждого члена и укажите [`offset`](https://python-all.ru/3.16/c-api/structures.html#c.PyMemberDef.offset) относительно вашей новой структуры.376377### Доступ к данным пользовательского типа378379Далее начинается самое сложное: во всём коде, которому нужен доступ к этой структуре, потребуется дополнительный вызов [`PyObject_GetTypeData()`](https://python-all.ru/3.16/c-api/object.html#c.PyObject_GetTypeData) для получения указателя `CustomObjectData *` из `PyObject *`:380381```c382PyObject *obj = ...;383CustomObjectData *data = PyObject_GetTypeData(obj, cls);384```385386Обратите внимание: этот вызов требует *объект типа* вашего класса (`cls`).387388Если ваш класс не допускает наследования (то есть не использует флаг [`Py_TPFLAGS_BASETYPE`](https://python-all.ru/3.16/c-api/typeobj.html#c.Py_TPFLAGS_BASETYPE)), то `cls` будет `Py_TYPE(obj)`. В противном случае **НЕ ИСПОЛЬЗУЙТЕ** `Py_TYPE` с `PyObject_GetTypeData()`: он может вернуть память, зарезервированную для постороннего подкласса! Например, если пользователь создаст такой подкласс:389390```python391class Sub(YourCustomClass):392   __slots__ = ('a', 'b')393```394395то `Py_TYPE(obj)` равно `Sub`, и лежащая в основе память может выглядеть так:396397```text398╭─ PyObject *obj399│              ╭─ the pointer you want400│              │                    ╭─ PyObject_GetTypeData(obj, Py_TYPE(obj))401▼              ▼                    ▼402┌──────────┬───┬────────────────┬───┬─────────────┬───┬─────────────┐403│ PyObject │...│ CustomTypeData │...│ PyObject *a │...│ PyObject *b │404└──────────┴───┴────────────────┴───┴─────────────┴───┴─────────────┘405```406407(Многоточия обозначают возможное выравнивание. Обратите внимание: такое расположение в памяти не гарантируется – будущие версии Python могут добавить другое выравнивание или даже изменить порядок структур.)408409Есть два основных способа получить правильный класс:410411- В методах экземпляра ваша реализация может использовать сигнатуру [`PyCMethod`](https://python-all.ru/3.16/c-api/structures.html#c.PyCMethod) (и бит [`METH_METHOD`](https://python-all.ru/3.16/c-api/structures.html#c.METH_METHOD) в [`PyMethodDef.ml_flags`](https://python-all.ru/3.16/c-api/structures.html#c.PyMethodDef.ml_flags)), получая класс как аргумент `defining_class`.412- В противном случае дайте вашему классу уникальный статический токен через слот [`Py_tp_token`](https://python-all.ru/3.16/c-api/type.html#c.Py_tp_token) и используйте:413414  ```c415  PyTypeObject cls;416  if (PyType_GetBaseByToken(Py_TYPE(obj), my_tp_token, &cls) < 0) {417      /* обработать ошибку */418  }419  CustomObjectData *data = PyObject_GetTypeData(obj, cls);420  ```421422  Токены типов работают так же, как токены модулей, описанные [ранее в этом руководстве](https://python-all.ru/3.16/howto/abi3t-migration.html#abi3t-migration-module-token).423424## Избегайте условной компиляции425426Проверьте свой код на наличие API, определяющего версию Python, под которую *собран* ваш модуль расширения. Больше это не соответствует той версии Python, на которой модуль выполняется, поэтому такой код часто требует изменений. Ищите следующие макросы:427428- [`PY_VERSION_HEX`](https://python-all.ru/3.16/c-api/apiabiversion.html#c.PY_VERSION_HEX), [`PY_MAJOR_VERSION`](https://python-all.ru/3.16/c-api/apiabiversion.html#c.PY_MAJOR_VERSION), [`PY_MINOR_VERSION`](https://python-all.ru/3.16/c-api/apiabiversion.html#c.PY_MINOR_VERSION):429430  - Чтобы получить версию времени выполнения, используйте [`Py_Version`](https://python-all.ru/3.16/c-api/apiabiversion.html#c.Py_Version);431  - Чтобы определить, какой C API доступен, используйте [`Py_TARGET_ABI3T`](https://python-all.ru/3.16/c-api/stable.html#c.Py_TARGET_ABI3T). Этот макрос устанавливается в минимальную поддерживаемую версию.432- [`Py_GIL_DISABLED`](https://python-all.ru/3.16/using/configure.html#c.Py_GIL_DISABLED): в `abi3t` этот макрос всегда определён. Код, работающий со свободно-поточной версией Python, *должен* также работать и с включённым GIL (поскольку GIL может быть включён во время выполнения), и обычно *работает* (если только по какой-то причине ему не требуется более одного [прикреплённого состояния потока](https://python-all.ru/3.16/glossary.html#term-attached-thread-state) одновременно).433434## Дальнейшие изменения кода435436Если у вас всё ещё остаются ошибки или предупреждения компилятора, найдите способ их исправить. Увы, это руководство ограничено и не может охватить все возможные изменения кода, которые могут потребоваться расширениям.437438Если вы столкнулись с проблемой, с которой могут встретиться другие авторы расширений, рассмотрите возможность [сообщить о проблеме](https://python-all.ru/3.16/bugs.html#reporting-documentation-bugs) (или отправить pull request) для этого руководства.439440Возможно, вашу проблему невозможно исправить в текущей версии `abi3t`. В таком случае сообщение о ней может помочь расставить приоритеты для следующей версии CPython.441442## Тегирование и распространение443444Если вы используете инструмент сборки с поддержкой `abi3t`, ваше расширение готово, но вы, возможно, захотите проверить, что оно собрано правильно.445446Расширения, собранные с `abi3t`, должны иметь следующее расширение:447448- В Windows: `.pyd` (как и любое другое расширение);449- Linux, macOS и другие системы, использующие суффикс `.so`: `.abi3t.so` (**не** `.cpython-315t.so` или `.abi3.so`). Обратите внимание, что как свободно-поточные, так и несвободно-поточные сборки будут загружать расширения `.abi3t.so`;450- Другие системы: обратитесь к своему дистрибьютору и, возможно, обновите это руководство.451452Если вы распространяете расширение в виде *wheel*, используйте следующие теги:453454- Тег Python: `cp3XX`, где *XX* – минимальная версия Python, для которой собрано расширение. (Например, `cp315`, если вы установили `Py_TARGET_ABI3T` в `0x30f0000`. См. [Компиляция для стабильного ABI](https://python-all.ru/3.16/c-api/stable.html#abi3-compiling) для других значений.)455- Тег ABI: `abi3.abi3t`. Это *сжатый набор тегов*, который указывает на поддержку как несвободно-поточных, так и свободно-поточных сборок.456457Например, имя файла wheel может выглядеть так:458459```text460myproject-1.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl461```462463> **См. также**464>465> [Теги совместимости платформ](https://python-all.ru/3.16/howto/abi3t-migration.html) в метаданных распространения пакетов PyPA.466467Если имя файла или теги неверны, исправьте их.468469## Тестирование470471Обратите внимание: при сборке расширения, совместимого с несколькими версиями CPython, следует всегда *тестировать* его с каждой поддерживаемой версией (например, 3.15, 3.16 и так далее). Стабильный ABI гарантирует только *ABI*-совместимость; возможны также изменения поведения – как намеренные (описанные в [**PEP 387**](https://python-all.ru/3.16/howto/abi3t-migration.html)), так и ошибки.472473Обязательно запускайте тесты как на свободно-поточной, так и на несвободно-поточной сборках CPython.474475Если они проходят, поздравляем! У вас есть расширение `abi3t`.476