extension-modules.md
1> **Источник:** https://python-all.ru/3.15/c-api/extension-modules.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Определение модулей расширения89Расширение на C для CPython – это разделяемая библиотека (например, файл `.so` в Linux, `.pyd` DLL в Windows), которая загружается в процесс Python (например, скомпилирована с совместимыми настройками компилятора) и экспортирует функцию *экспортного хука* (или старую [функцию инициализации](https://python-all.ru/3.15/c-api/extension-modules.html#extension-pyinit)).1011Чтобы библиотека импортировалась по умолчанию (то есть с помощью [`importlib.machinery.ExtensionFileLoader`](https://python-all.ru/3.15/library/importlib.html#importlib.machinery.ExtensionFileLoader)), она должна быть доступна в [`sys.path`](https://python-all.ru/3.15/library/sys.html#sys.path), и её имя должно соответствовать имени модуля с расширением, указанным в [`importlib.machinery.EXTENSION_SUFFIXES`](https://python-all.ru/3.15/library/importlib.html#importlib.machinery.EXTENSION_SUFFIXES).1213> **Примечание**14>15> Сборку, упаковку и распространение модулей расширения лучше выполнять с помощью сторонних инструментов, и это выходит за рамки данного документа. Подходящим инструментом является Setuptools, документация которого находится по адресу [https://setuptools.pypa.io/en/latest/setuptools.html](https://python-all.ru/3.15/c-api/extension-modules.html).1617## Экспортный хук расширения1819Добавлено в версии 3.15: Поддержка `PyModExport_<name>` экспортного хука была добавлена в Python 3.15. Старый способ определения модулей по-прежнему доступен: обратитесь к разделу [функции PyInit](https://python-all.ru/3.15/c-api/extension-modules.html#extension-pyinit) или к более ранним версиям этой документации, если вы планируете поддерживать более старые версии Python.2021Экспортный хук должен быть экспортируемой функцией со следующей сигнатурой:2223#### `PySlot *PyModExport_modulename(void)`2425Для модулей, имена которых содержат только ASCII, [экспортный хук](https://python-all.ru/3.15/c-api/extension-modules.html#extension-export-hook) должен называться `PyModExport_<name>`, где `<name>` заменяется на имя модуля.2627Для модулей с не-ASCII именами экспортный хук должен называться `PyModExportU_<name>` (обратите внимание на `U`), где `<name>` кодируется с помощью кодировки *punycode* Python, а дефисы заменяются на символы подчеркивания. В Python:2829```python30def hook_name(name):31 try:32 suffix = b'_' + name.encode('ascii')33 except UnicodeEncodeError:34 suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')35 return b'PyModExport' + suffix36```3738Экспортный хук возвращает массив из [`PySlot`](https://python-all.ru/3.15/c-api/slots.html#c.PySlot) записей, завершающийся записью с идентификатором слота `0`. Эти слоты описывают, как модуль должен быть создан и инициализирован.3940Этот массив должен оставаться действительным и постоянным до завершения работы интерпретатора. Обычно он должен использовать хранилище `static`. Для любого динамического поведения предпочтительнее использовать слоты [`Py_mod_create`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_create) и [`Py_mod_exec`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_exec).4142Экспортный хук может вернуть `NULL` с установленным исключением для сигнализации об ошибке.4344Рекомендуется определять функцию экспортного хука с помощью вспомогательного макроса:4546#### `PyMODEXPORT_FUNC`4748*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.15.*4950Объявляет экспортный хук модуля расширения. Этот макрос:5152- определяет тип возвращаемого значения [PySlot](https://python-all.ru/3.15/c-api/slots.html#c.PySlot)\*,53- добавляет любые специальные объявления компоновки, требуемые платформой, и54- для C++ объявляет функцию как `extern "C"`.5556Например, модуль с именем `spam` был бы определён так:5758```c59PyABIInfo_VAR(abi_info);6061static PySlot spam_slots[] = {62 PySlot_STATIC_DATA(Py_mod_abi, &abi_info),63 PySlot_STATIC_DATA(Py_mod_name, "spam"),64 PySlot_FUNC(Py_mod_init, spam_init_function),65 ...66 PySlot_END67};6869PyMODEXPORT_FUNC70PyModExport_spam(void)71{72 return spam_slots;73}74```7576Экспортный хук обычно – единственный не-`static` элемент, определённый в C-исходном коде модуля.7778Хук должен быть коротким. Если он делает больше, чем `return` статический массив, применяются несколько предостережений:7980- Если нужно использовать любой Python C API, рекомендуется сначала вызвать [`PyABIInfo_Check()`](https://python-all.ru/3.15/c-api/stable.html#c.PyABIInfo_Check), чтобы возбудить исключение, а не упасть, в распространённых случаях несовместимости ABI.81- Код в экспортном хуке никогда не должен полагаться на [GIL](https://python-all.ru/3.15/glossary.html#term-GIL): [сборки со свободными потоками](https://python-all.ru/3.15/glossary.html#term-free-threaded-build) Python могут проверять слот [`Py_mod_gil`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_gil) (или его отсутствие) только после возврата хука,82- Аналогично, хук может быть вызван в любом суб-интерпретаторе, так как слот [`Py_mod_multiple_interpreters`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_multiple_interpreters) (или его отсутствие) проверяется только после возврата хука.8384Например:8586```c87PyMODEXPORT_FUNC88PyModExport_modulename(void)89{90 if (PyABIInfo_Check(&abi_info, "modulename") < 0) {91 /* Несовместимость ABI. Проверять возникшее исключение небезопасно. */92 return NULL;93 }9495 /* используйте Python API (как можно реже); не полагайтесь на GIL. */9697 return modulename_slots;98}99```100101> **Примечание**102>103> It is possible to export multiple modules from a single shared library by defining multiple export hooks. However, importing them requires a custom importer or suitably named copies/links of the extension file, because Python’s import machinery only finds the function corresponding to the filename. See the [Multiple modules in one library](https://python-all.ru/3.15/c-api/extension-modules.html) section in [**PEP 489**](https://python-all.ru/3.15/c-api/extension-modules.html) for details.104105## Многофазная инициализация106107Процесс создания модуля расширения состоит из нескольких фаз:108109- Python находит и вызывает экспортный хук, чтобы получить информацию о том, как создать модуль.110- Перед выполнением любого существенного кода Python может определить, какие возможности поддерживает модуль, и может настроить окружение или отказаться загружать несовместимое расширение. Слоты, такие как [`Py_mod_abi`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_abi), [`Py_mod_gil`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_gil) и [`Py_mod_multiple_interpreters`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_multiple_interpreters), влияют на этот шаг.111- По умолчанию Python сам создаёт объект модуля – то есть делает эквивалент вызова [`__new__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__new__) при создании объекта. Этот шаг можно переопределить с помощью слота [`Py_mod_create`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_create).112- Python устанавливает начальные атрибуты модуля, такие как [`__package__`](https://python-all.ru/3.15/reference/datamodel.html#module.__package__) и [`__loader__`](https://python-all.ru/3.15/reference/datamodel.html#module.__loader__), и вставляет объект модуля в [`sys.modules`](https://python-all.ru/3.15/library/sys.html#sys.modules).113- После этого объект модуля инициализируется специфичным для расширения способом – эквивалентом [`__init__()`](https://python-all.ru/3.15/reference/datamodel.html#object.__init__) при создании объекта или выполнения кода верхнего уровня в модуле на Python. Поведение задаётся с помощью слота [`Py_mod_exec`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_exec).114115This is called *multi-phase initialization* to distinguish it from the legacy (but still supported) [single-phase initialization](https://python-all.ru/3.15/c-api/extension-modules.html#single-phase-initialization), where an initialization function returns a fully constructed module.116117Changed in version 3.5: Added support for multi-phase initialization ([**PEP 489**](https://python-all.ru/3.15/c-api/extension-modules.html)).118119## Несколько экземпляров модуля120121По умолчанию модули расширения не являются синглтонами. Например, если запись [`sys.modules`](https://python-all.ru/3.15/library/sys.html#sys.modules) удалена и модуль импортируется заново, создаётся новый объект модуля и, как правило, заполняется новыми объектами методов и типов. Старый модуль подлежит обычной сборке мусора. Это повторяет поведение чисто Python-модулей.122123Additional module instances may be created in [sub-interpreters](https://python-all.ru/3.15/c-api/subinterpreters.html#sub-interpreter-support) or after Python runtime reinitialization ([`Py_Finalize()`](https://python-all.ru/3.15/c-api/interp-lifecycle.html#c.Py_Finalize) and [`Py_Initialize()`](https://python-all.ru/3.15/c-api/interp-lifecycle.html#c.Py_Initialize)). In these cases, sharing Python objects between module instances would likely cause crashes or undefined behavior.124125To avoid such issues, each instance of an extension module should be *isolated*: changes to one instance should not implicitly affect the others, and all state owned by the module, including references to Python objects, should be specific to a particular module instance. See [Isolating Extension Modules](https://python-all.ru/3.15/howto/isolating-extensions.html#isolating-extensions-howto) for more details and a practical guide.126127A simpler way to avoid these issues is [raising an error on repeated initialization](https://python-all.ru/3.15/howto/isolating-extensions.html#isolating-extensions-optout).128129All modules are expected to support [sub-interpreters](https://python-all.ru/3.15/c-api/subinterpreters.html#sub-interpreter-support), or otherwise explicitly signal a lack of support. This is usually achieved by isolation or blocking repeated initialization, as above. A module may also be limited to the main interpreter using the [`Py_mod_multiple_interpreters`](https://python-all.ru/3.15/c-api/module.html#c.Py_mod_multiple_interpreters) slot.130131## `PyInit` функция132133[Soft deprecated](https://python-all.ru/3.15/glossary.html#term-soft-deprecated) since version 3.15: This functionality will not get new features, but there are no plans to remove it.134135Вместо [`PyModExport_modulename()`](https://python-all.ru/3.15/c-api/extension-modules.html#c.PyModExport_modulename) модуль расширения может определить *функцию инициализации* старого стиля со следующей сигнатурой:136137#### `PyObject *PyInit_modulename(void)`138139Its name should be `PyInit_<name>`, with `<name>` replaced by the name of the module. For non-ASCII module names, use `PyInitU_<name>` instead, with `<name>` encoded in the same way as for the [export hook](https://python-all.ru/3.15/c-api/extension-modules.html#extension-export-hook) (that is, using Punycode with underscores).140141Если модуль экспортирует и `PyInit_<name>`, и `PyModExport_<name>`, функция `PyInit_<name>` игнорируется.142143Как и в случае с [`PyMODEXPORT_FUNC`](https://python-all.ru/3.15/c-api/extension-modules.html#c.PyMODEXPORT_FUNC), рекомендуется определять функцию инициализации с помощью вспомогательного макроса:144145#### `PyMODINIT_FUNC`146147Объявляет функцию инициализации модуля расширения. Этот макрос:148149- задаёт возвращаемый тип [PyObject](https://python-all.ru/3.15/c-api/structures.html#c.PyObject)\*,150- добавляет любые специальные объявления компоновки, требуемые платформой, и151- для C++ объявляет функцию как `extern "C"`.152153Обычно функция инициализации (`PyInit_modulename`) возвращает экземпляр [`PyModuleDef`](https://python-all.ru/3.15/c-api/module.html#c.PyModuleDef) с не-`NULL` [`m_slots`](https://python-all.ru/3.15/c-api/module.html#c.PyModuleDef.m_slots). Это позволяет Python использовать [многофазную инициализацию](https://python-all.ru/3.15/c-api/extension-modules.html#multi-phase-initialization).154155Перед возвратом экземпляр `PyModuleDef` должен быть инициализирован с помощью следующей функции:156157#### `PyObject *PyModuleDef_Init(PyModuleDef *def)`158159*Часть [Stable ABI](https://python-all.ru/3.15/c-api/stable.html#stable) начиная с версии 3.5.*160161Гарантирует, что определение модуля является правильно инициализированным объектом Python, который корректно сообщает свой тип и счетчик ссылок.162163Возвращает *def*, приведённый к `PyObject*`, или `NULL` в случае ошибки.164165Вызов этой функции обязателен перед возвратом [`PyModuleDef`](https://python-all.ru/3.15/c-api/module.html#c.PyModuleDef) из функции инициализации модуля. Не следует использовать её в других контекстах.166167Обратите внимание, что Python предполагает, что структуры `PyModuleDef` выделены статически. Эта функция может вернуть как новую ссылку, так и заимствованную; эту ссылку нельзя освобождать.168169Добавлено в версии 3.5.170171Например, модуль с именем `spam` был бы определён так:172173```c174static struct PyModuleDef spam_module = {175 .m_base = PyModuleDef_HEAD_INIT,176 .m_name = "spam",177 ...178};179180PyMODINIT_FUNC181PyInit_spam(void)182{183 return PyModuleDef_Init(&spam_module);184}185```186187### Устаревшая однофазная инициализация188189[Мягко устарело](https://python-all.ru/3.15/glossary.html#term-soft-deprecated) начиная с версии 3.15: Однофазная инициализация – это устаревший механизм инициализации модулей расширения с известными недостатками и ошибками проектирования. Авторам модулей расширения рекомендуется вместо этого использовать многофазную инициализацию.190191Однако планов по удалению поддержки этого механизма нет.192193При однофазной инициализации [функция инициализации старого образца](https://python-all.ru/3.15/c-api/extension-modules.html#extension-pyinit) (`PyInit_modulename`) должна создать, заполнить и вернуть объект модуля. Обычно это делается с помощью [`PyModule_Create()`](https://python-all.ru/3.15/c-api/module.html#c.PyModule_Create) и таких функций, как [`PyModule_AddObjectRef()`](https://python-all.ru/3.15/c-api/module.html#c.PyModule_AddObjectRef).194195Однофазная инициализация отличается от [стандартной](https://python-all.ru/3.15/c-api/extension-modules.html#multi-phase-initialization) следующим образом:196197- Однофазные модули являются, или, точнее, *содержат* «одиночки» (синглтоны).198199 При первой инициализации модуля Python сохраняет содержимое `__dict__` модуля (то есть, как правило, функции и типы модуля).200201 При последующих импортах Python не вызывает функцию инициализации снова. Вместо этого он создаёт новый объект модуля с новым `__dict__` и копирует в него сохранённое содержимое. Например, для однофазного модуля `_testsinglephase` [\[1\]](https://python-all.ru/3.15/c-api/extension-modules.html#testsinglephase), который определяет функцию `sum` и класс исключения `error`:202203 ```python204 >>> import sys205 >>> import _testsinglephase as one206 >>> del sys.modules['_testsinglephase']207 >>> import _testsinglephase as two208 >>> one is two209 False210 >>> one.__dict__ is two.__dict__211 False212 >>> one.sum is two.sum213 True214 >>> one.error is two.error215 True216 ```217218 Точное поведение следует считать деталью реализации CPython.219- Чтобы обойти тот факт, что `PyInit_modulename` не принимает аргумент *spec*, некоторое состояние механизма импорта сохраняется и применяется к первому подходящему модулю, созданному во время вызова `PyInit_modulename`. В частности, при импорте подмодуля этот механизм добавляет имя родительского пакета к имени модуля.220221 Функция `PyInit_modulename` однофазного модуля должна создать «свой» объект модуля как можно раньше, до того как могут быть созданы любые другие объекты модулей.222- Не-ASCII имена модулей (`PyInitU_modulename`) не поддерживаются.223- Однофазные модули поддерживают функции поиска модулей, такие как [`PyState_FindModule()`](https://python-all.ru/3.15/c-api/module.html#c.PyState_FindModule).224- [`PyModuleDef.m_slots`](https://python-all.ru/3.15/c-api/module.html#c.PyModuleDef.m_slots) модуля должен быть NULL.225226\[[1](https://python-all.ru/3.15/c-api/extension-modules.html#id3)\]227228`_testsinglephase` – это внутренний модуль, используемый в наборе тестов CPython; ваша установка может включать его, а может и нет.229