Расширение Python с помощью C или C++
Добавить в Python новые встроенные модули довольно просто, если вы умеете программировать на C. Такие модули расширения могут делать две вещи, которые нельзя выполнить непосредственно в Python: реализовывать новые встроенные типы объектов и вызывать функции библиотек C и системные вызовы.
Для поддержки расширений API Python (интерфейс прикладного программирования) определяет набор функций, макросов и переменных, предоставляющих доступ к большинству аспектов среды выполнения Python. API Python подключается к файлу исходного кода C с помощью заголовочного файла "Python.h".
Компиляция модуля расширения зависит от предполагаемого способа его использования, а также от конфигурации вашей системы; подробности приведены в последующих главах.
Примечание
Интерфейс расширений C предназначен только для CPython, поэтому модули расширения не работают в других реализациях Python. Во многих случаях можно обойтись без написания расширений C и сохранить переносимость на другие реализации. Например, если вам нужно вызывать функции библиотек C или системные вызовы, рассмотрите возможность использования модуля ctypes или библиотеки cffi, а не написания собственного кода C. Эти модули позволяют писать код на Python для взаимодействия с кодом C и обеспечивают большую переносимость между реализациями Python, чем написание и компиляция модуля расширения C.
1.1. Простой пример
Создадим модуль расширения под названием spam (любимая еда поклонников Монти Пайтона…) и предположим, что мы хотим создать интерфейс Python к функции библиотеки C system() [1]. Эта функция принимает в качестве аргумента строку символов, завершённую нулевым символом, и возвращает целое число. Мы хотим, чтобы эту функцию можно было вызывать из Python следующим образом:
>>> import spam
>>> status = spam.system("ls -l")
Для начала создадим файл spammodule.c. (Исторически, если модуль называется spam, файл C, содержащий его реализацию, называется spammodule.c; если имя модуля очень длинное, например spammify, имя файла модуля может быть просто spammify.c.)
Первые две строки нашего файла могут выглядеть так:
#define PY_SSIZE_T_CLEAN #include <Python.h>
они подключают API Python (при желании можно добавить комментарий с описанием назначения модуля и уведомление об авторских правах).
Примечание
Поскольку Python может определять некоторые макросы препроцессора, влияющие на стандартные заголовочные файлы в отдельных системах, Python.h необходимо подключать до любых стандартных заголовочных файлов.
#define PY_SSIZE_T_CLEAN использовался, чтобы указать, что в некоторых API вместо int следует использовать Py_ssize_t. Начиная с Python 3.13 он не требуется, но мы оставляем его здесь для обратной совместимости. Описание этого макроса см. в разделе Строки и буферы.
Все видимые пользователю символы, определённые в Python.h, имеют префикс Py или PY, за исключением символов, определённых в стандартных заголовочных файлах.
Совет
Для обратной совместимости Python.h включает несколько стандартных заголовочных файлов. Расширения C должны подключать используемые ими стандартные заголовочные файлы и не должны полагаться на их неявное подключение. При использовании ограниченного API C версии 3.13 или новее неявно подключаются следующие заголовочные файлы:
<assert.h>-
<intrin.h>(в Windows) <inttypes.h><limits.h><math.h><stdarg.h><wchar.h>-
<sys/types.h>(если присутствует)
Если Py_LIMITED_API не определён или установлен в версию 3.12 либо более раннюю, также подключаются следующие заголовочные файлы:
<ctype.h>-
<unistd.h>(в POSIX)
Если Py_LIMITED_API не определён или установлен в версию 3.10 либо более раннюю, также подключаются следующие заголовочные файлы:
<errno.h><stdio.h><stdlib.h><string.h>
Следующее, что мы добавим в файл модуля, — функция C, которая будет вызываться при вычислении выражения Python spam.system(string) (вскоре мы увидим, как именно она вызывается):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
Список аргументов Python (например, единственное выражение "ls -l") напрямую преобразуется в аргументы, передаваемые функции C. Функция C всегда принимает два аргумента, которые традиционно называются self и args.
Аргумент self указывает на объект модуля для функций уровня модуля; для метода он указывал бы на экземпляр объекта.
Аргумент args будет указателем на объект-кортеж Python, содержащий аргументы. Каждый элемент кортежа соответствует аргументу в списке аргументов вызова. Аргументы являются объектами Python — чтобы использовать их в нашей функции C, необходимо преобразовать их в значения C. Функция PyArg_ParseTuple() из API Python проверяет типы аргументов и преобразует их в значения C. Она использует строку шаблона, чтобы определить требуемые типы аргументов, а также типы переменных C, в которые следует поместить преобразованные значения. Подробнее об этом позже.
PyArg_ParseTuple() возвращает true (ненулевое значение), если все аргументы имеют правильный тип и их составляющие сохранены в переменных, адреса которых переданы функции. Если передан недопустимый список аргументов, функция возвращает false (нулевое значение). В последнем случае она также возбуждает подходящее исключение, чтобы вызывающая функция могла немедленно вернуть NULL (как мы видели в примере).
1.2. Отступление: ошибки и исключения
В интерпретаторе Python действует важное соглашение: при сбое функции она должна установить условие исключения и вернуть значение ошибки (обычно -1 или указатель NULL). Информация об исключении хранится в трёх полях состояния потока интерпретатора. Если исключения нет, эти поля равны NULL. В противном случае они содержат эквиваленты на C элементов кортежа Python, возвращаемого функцией sys.exc_info(). Это тип исключения, экземпляр исключения и объект трассировки стека. Чтобы понимать, как передаются ошибки, важно знать об этих полях.
API Python определяет ряд функций для установки исключений различных типов.
Наиболее распространённая из них — PyErr_SetString(). Она принимает объект исключения и строку C. Объект исключения обычно является предопределённым объектом, например PyExc_ZeroDivisionError. Строка C указывает причину ошибки, преобразуется в строковый объект Python и сохраняется как «связанное значение» исключения.
Другая полезная функция — PyErr_SetFromErrno(): она принимает только аргумент-исключение и формирует связанное значение, анализируя глобальную переменную errno. Самой универсальной является функция PyErr_SetObject(), принимающая два объектных аргумента: исключение и связанное с ним значение. Передавать функции Py_INCREF() для объектов, передаваемых любой из этих функций, не нужно.
Проверить, было ли установлено исключение, не изменяя состояние, можно с помощью функции PyErr_Occurred(). Она возвращает текущий объект исключения или NULL, если исключения не возникло. Обычно нет необходимости вызывать PyErr_Occurred(), чтобы выяснить, произошла ли ошибка при вызове функции, поскольку это должно быть понятно по возвращаемому значению.
Если функция f, вызывающая другую функцию g, обнаруживает, что последняя завершилась с ошибкой, функция f сама должна вернуть значение ошибки (обычно NULL или -1). Она не должна вызывать одну из функций PyErr_* — функция g уже вызвала такую функцию. Затем вызывающая функция для f также должна вернуть признак ошибки своей вызывающей функции, опять же не вызывая PyErr_*, и так далее: наиболее подробную причину ошибки уже сообщила функция, которая первой её обнаружила. Когда ошибка достигает главного цикла интерпретатора Python, выполнение текущего кода Python прерывается, после чего интерпретатор пытается найти обработчик исключения, указанный программистом на Python.
(В некоторых ситуациях модуль может сообщить более подробную ошибку, вызвав ещё одну функцию PyErr_*; в таких случаях это допустимо. Однако в общем случае в этом нет необходимости, и это может привести к потере информации о причине ошибки: большинство операций может завершиться с ошибкой по разным причинам.)
Чтобы проигнорировать исключение, установленное при неудачном вызове функции, необходимо явно сбросить состояние исключения с помощью вызова PyErr_Clear(). Единственный случай, когда код C должен вызывать PyErr_Clear(), — если он не хочет передавать ошибку интерпретатору, а намерен полностью обработать её самостоятельно (возможно, попробовав что-то ещё или сделав вид, что ничего не произошло).
Каждый неудачный вызов malloc() должен приводить к исключению — непосредственный вызывающий код для malloc() (или realloc()) должен вызвать PyErr_NoMemory() и сам вернуть признак ошибки. Все функции, создающие объекты (например, PyLong_FromLong()), уже делают это, поэтому это замечание относится только к тем, кто вызывает malloc() напрямую.
Также обратите внимание, что функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль в случае успеха и -1 в случае ошибки, как и системные вызовы Unix. Важное исключение составляют PyArg_ParseTuple() и родственные функции.
Наконец, не забывайте убирать мусор (вызывая Py_XDECREF() или Py_DECREF() для уже созданных объектов), когда возвращаете признак ошибки!
Выбор исключения, которое следует возбудить, полностью зависит от вас. Для всех встроенных исключений Python существуют предварительно объявленные объекты C, например PyExc_ZeroDivisionError, которые можно использовать напрямую. Разумеется, выбирать исключения нужно разумно: не используйте PyExc_TypeError для сообщения о том, что файл не удалось открыть (для этого, вероятно, подойдёт PyExc_OSError). Если со списком аргументов что-то не так, функция PyArg_ParseTuple() обычно возбуждает исключение PyExc_TypeError. Если значение аргумента должно находиться в определённом диапазоне или удовлетворять другим условиям, подойдёт PyExc_ValueError.
Также можно определить новое исключение, уникальное для вашего модуля. Проще всего объявить в начале файла статическую глобальную переменную-объект:
static PyObject *SpamError = NULL;
и инициализировать её вызовом PyErr_NewException() в функции Py_mod_exec модуля (spam_module_exec()):
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Поскольку SpamError — глобальная переменная, при каждом повторном вызове функции Py_mod_exec она будет перезаписана.
Пока обойдём эту проблему: запретим повторную инициализацию, возбуждая исключение ImportError:
static PyObject *SpamError = NULL;
static int
spam_module_exec(PyObject *m)
{
if (SpamError != NULL) {
PyErr_SetString(PyExc_ImportError,
"cannot initialize spam module more than once");
return -1;
}
SpamError = PyErr_NewException("spam.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot spam_module_slots[] = {
{Py_mod_exec, spam_module_exec},
{0, NULL}
};
static struct PyModuleDef spam_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "spam",
.m_size = 0, // non-negative
.m_slots = spam_module_slots,
};
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Обратите внимание, что имя объекта исключения в Python — spam.error. Функция PyErr_NewException() может создать класс, базовым классом которого будет Exception (если вместо NULL не передан другой класс); см. раздел Встроенные исключения.
Обратите также внимание, что переменная SpamError сохраняет ссылку на вновь созданный класс исключения; это сделано намеренно! Поскольку внешний код может удалить исключение из модуля, необходимо владеть ссылкой на класс, чтобы он не был уничтожен, в результате чего SpamError стал бы висячим указателем. Если это произойдёт, код C, возбуждающий исключение, может привести к аварийному завершению программы или другим непредвиденным последствиям.
Пока отсутствует вызов Py_DECREF(), который удалил бы эту ссылку. Даже при завершении работы интерпретатора Python глобальная переменная SpamError не будет собрана сборщиком мусора. Она «утечёт». Однако мы гарантировали, что это произойдёт не более одного раза за процесс.
Позже в этом примере мы обсудим использование PyMODINIT_FUNC в качестве типа возвращаемого значения функции.
Исключение spam.error можно возбудить в модуле расширения вызовом PyErr_SetString(), как показано ниже:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
1.3. Вернёмся к примеру
Теперь вы должны понимать следующий фрагмент нашей примерной функции:
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
При обнаружении ошибки в списке аргументов он возвращает NULL (признак ошибки для функций, возвращающих указатели на объекты), полагаясь на исключение, установленное функцией PyArg_ParseTuple(). В противном случае строковое значение аргумента копируется в локальную переменную command. Это присваивание указателя; строку, на которую он указывает, изменять нельзя (поэтому в стандартном C переменную command следовало бы объявить как const char
*command).
Следующая инструкция — вызов функции Unix system() с передачей ей строки, которую мы только что получили из PyArg_ParseTuple():
sts = system(command);
Наша функция spam.system() должна вернуть значение sts в виде объекта Python. Для этого используется функция PyLong_FromLong().
return PyLong_FromLong(sts);
В этом случае она вернёт целочисленный объект. (Да, в Python даже целые числа являются объектами в куче!)
Если у вас есть функция C, не возвращающая полезного значения (функция с типом возвращаемого значения void), соответствующая функция Python должна возвращать None. Для этого используется следующий приём, реализованный макросом Py_RETURN_NONE:
Py_INCREF(Py_None); return Py_None;
Py_None — это имя специального объекта Python None в C. Это полноценный объект Python, а не указатель NULL, который в большинстве контекстов означает «ошибка», как мы уже видели.
1.4. Таблица методов модуля и функция инициализации
Я обещал показать, как spam_system() вызывается из программ Python. Сначала нужно указать его имя и адрес в «таблице методов»:
static PyMethodDef spam_methods[] = {
...
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
...
{NULL, NULL, 0, NULL} /* Sentinel */
};
Обратите внимание на третью запись (METH_VARARGS). Это флаг, сообщающий интерпретатору, какое соглашение о вызовах следует использовать для функции C. Обычно это всегда должно быть METH_VARARGS или METH_VARARGS | METH_KEYWORDS; значение 0 означает, что используется устаревший вариант функции PyArg_ParseTuple().
При использовании только METH_VARARGS функция должна ожидать, что параметры уровня Python будут переданы в виде кортежа, пригодного для разбора с помощью PyArg_ParseTuple(); подробнее об этой функции рассказано ниже.
Бит METH_KEYWORDS можно установить в третьем поле, если функции нужно передавать именованные аргументы. В этом случае функция C должна принимать третий параметр PyObject *, представляющий собой словарь именованных аргументов. Для разбора аргументов такой функции используйте PyArg_ParseTupleAndKeywords().
На структуру определения модуля должна ссылаться таблица методов:
static struct PyModuleDef spam_module = {
...
.m_methods = spam_methods,
...
};
В свою очередь, эту структуру необходимо передать интерпретатору в функции инициализации модуля. Функция инициализации должна называться PyInit_name(), где name — имя модуля; она должна быть единственным элементом, определённым в файле модуля, который не является static:
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
Обратите внимание, что PyMODINIT_FUNC объявляет функцию с типом возвращаемого значения PyObject *, задаёт специальные объявления связывания, требуемые платформой, а для C++ объявляет функцию как extern "C".
Функция PyInit_spam() вызывается при первом импорте каждым интерпретатором своего модуля spam. (Замечания о встраивании Python приведены ниже.) Указатель на определение модуля необходимо вернуть через PyModuleDef_Init(), чтобы механизм импорта мог создать модуль и сохранить его в sys.modules.
При встраивании Python функция PyInit_spam() не вызывается автоматически, если только в таблице PyImport_Inittab нет соответствующей записи. Чтобы добавить модуль в таблицу инициализации, используйте PyImport_AppendInittab(), а затем при необходимости импортируйте модуль:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int
main(int argc, char *argv[])
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
}
// ... use Python C API here ...
return 0;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
Примечание
Если объявить глобальную переменную или локальную статическую переменную, при повторной инициализации модуля могут возникнуть непредвиденные побочные эффекты, например при удалении записей из sys.modules или импорте скомпилированных модулей в несколько интерпретаторов в рамках одного процесса (либо после вызова fork() без промежуточного вызова exec()). Если состояние модуля ещё не полностью изолировано, авторам следует рассмотреть возможность объявить, что модуль не поддерживает субинтерпретаторы (с помощью Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED).
Более подробный пример модуля включён в дистрибутив исходного кода Python в виде файла Modules/xxlimited.c. Этот файл можно использовать как шаблон или просто изучить в качестве примера.
1.5. Компиляция и компоновка
Прежде чем использовать новое расширение, нужно сделать ещё две вещи: скомпилировать его и скомпоновать с системой Python. При использовании динамической загрузки подробности могут зависеть от применяемого в вашей системе способа динамической загрузки; дополнительную информацию см. в главах о сборке модулей расширения (глава Сборка расширений C и C++) и о сборке в Windows (глава Сборка расширений C и C++ в Windows).
Если динамическую загрузку использовать нельзя или вы хотите сделать модуль постоянной частью интерпретатора Python, потребуется изменить настройки конфигурации и пересобрать интерпретатор. К счастью, в Unix это очень просто: поместите файл (например, spammodule.c) в каталог Modules/ распакованного дистрибутива исходного кода и добавьте в файл Modules/Setup.local строку с описанием вашего файла:
spam spammodule.o
а затем пересоберите интерпретатор, выполнив make в корневом каталоге. Можно также выполнить make в подкаталоге Modules/, но сначала нужно пересобрать там Makefile, выполнив команду «make Makefile». (Это необходимо делать каждый раз при изменении файла Setup.)
Если для компоновки модуля требуются дополнительные библиотеки, их также можно перечислить в строке файла конфигурации, например:
spam spammodule.o -lX11
1.6. Вызов функций Python из C
До сих пор мы сосредоточивались на том, как сделать функции C вызываемыми из Python. Полезно и обратное: вызывать функции Python из C. Это особенно актуально для библиотек, поддерживающих так называемые функции «обратного вызова». Если интерфейс C использует обратные вызовы, эквивалентная реализация на Python часто должна предоставлять программисту Python механизм обратного вызова; для его реализации потребуется вызывать функции обратного вызова Python из обратного вызова C. Можно представить и другие варианты использования.
К счастью, интерпретатор Python можно легко вызывать рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Я не буду подробно останавливаться на том, как вызвать синтаксический анализатор Python, передав ему определённую строку в качестве входных данных, — если вам это интересно, ознакомьтесь с реализацией параметра командной строки -c в Modules/main.c из исходного кода Python.)
Вызвать функцию Python несложно. Сначала программа на Python должна каким-либо образом передать вам объект-функцию Python. Для этого следует предоставить функцию (или какой-либо другой интерфейс). При вызове этой функции сохраните указатель на объект-функцию Python (не забудьте вызвать для него Py_INCREF()!) в глобальной переменной — или где-либо ещё, как вам удобно. Например, следующая функция может быть частью определения модуля:
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
Эту функцию необходимо зарегистрировать в интерпретаторе, используя флаг METH_VARARGS; это описано в разделе Таблица методов модуля и функция инициализации. Функция PyArg_ParseTuple() и её аргументы описаны в разделе Извлечение параметров в функциях расширения.
Макросы Py_XINCREF() и Py_XDECREF() увеличивают и уменьшают счётчик ссылок объекта и безопасны при наличии указателей NULL (но обратите внимание, что в этом контексте temp не будет NULL). Подробнее о них см. в разделе Счётчики ссылок.
Позже, когда придёт время вызвать функцию, вызовите функцию C PyObject_CallObject(). У этой функции два аргумента, оба — указатели на произвольные объекты Python: функция Python и список аргументов. Список аргументов всегда должен быть объектом-кортежем, длина которого равна числу аргументов. Чтобы вызвать функцию Python без аргументов, передайте NULL или пустой кортеж; чтобы вызвать её с одним аргументом, передайте кортеж с одним элементом. Py_BuildValue() возвращает кортеж, если строка формата состоит из нуля или более кодов формата, заключённых в скобки. Например:
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject() возвращает указатель на объект Python: это возвращаемое значение функции Python. Функция PyObject_CallObject() «не изменяет счётчики ссылок» своих аргументов. В примере был создан новый кортеж, который служит списком аргументов; сразу после вызова PyObject_CallObject() для него вызывается Py_DECREF().
Возвращаемое значение PyObject_CallObject() является «новым»: это либо совершенно новый объект, либо существующий объект с увеличенным счётчиком ссылок. Поэтому, если вы не хотите сохранить его в глобальной переменной, вам следует каким-либо образом вызвать для результата Py_DECREF() — даже (и особенно!) если его значение вам неинтересно.
Однако перед этим важно проверить, что возвращаемое значение не равно NULL. Если это так, функция Python завершилась, вызвав исключение. Если код C, вызвавший PyObject_CallObject(), был вызван из Python, теперь он должен вернуть вызывающему коду Python признак ошибки, чтобы интерпретатор мог вывести трассировку стека или вызывающий код Python мог обработать исключение. Если это невозможно или нежелательно, исключение следует сбросить вызовом PyErr_Clear(). Например:
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
В зависимости от желаемого интерфейса функции обратного вызова Python может также потребоваться передать список аргументов в PyObject_CallObject(). В некоторых случаях список аргументов также передаётся программой на Python через тот же интерфейс, который задаёт функцию обратного вызова. Тогда его можно сохранить и использовать так же, как объект-функцию. В других случаях может понадобиться создать новый кортеж для передачи в качестве списка аргументов. Проще всего это сделать вызовом Py_BuildValue(). Например, если требуется передать целочисленный код события, можно использовать следующий код:
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
Обратите внимание, что Py_DECREF(arglist) вызывается сразу после вызова функции и до проверки ошибки! Также имейте в виду, что, строго говоря, этот код не является полным: в Py_BuildValue() может закончиться память, и это необходимо проверять.
Также можно вызвать функцию с именованными аргументами, используя PyObject_Call(), которая поддерживает позиционные и именованные аргументы. Как и в предыдущем примере, для создания словаря используется Py_BuildValue().
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
1.7. Извлечение параметров в функциях расширения
Функция PyArg_ParseTuple() объявлена следующим образом:
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
Аргумент arg должен быть объектом-кортежем, содержащим список аргументов, переданный из Python функции C. Аргумент format должен быть строкой формата; её синтаксис описан в разделе Разбор аргументов и создание значений справочного руководства по API Python/C. Оставшиеся аргументы должны быть адресами переменных, тип которых определяется строкой формата.
Обратите внимание: хотя PyArg_ParseTuple() проверяет, что аргументы Python имеют требуемые типы, она не может проверить корректность адресов переданных вызову переменных C: если здесь допустить ошибку, программа, вероятно, аварийно завершится или как минимум перезапишет случайные участки памяти. Поэтому будьте осторожны!
Обратите внимание: любые переданные вызывающей стороне ссылки на объекты Python являются заимствованными; не уменьшайте их счётчик ссылок!
Несколько примеров вызова:
#define PY_SSIZE_T_CLEAN #include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* No arguments */
/* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
/* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
/* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* A pair of ints and a string, whose size is also returned */
/* Possible Python call: f((1, 2), 'three') */
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* A string, and optionally another string and an integer */
/* Possible Python calls:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* A rectangle and a point */
/* Possible Python call:
f(((0, 0), (400, 300)), (10, 10)) */
}
{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:myfunction", &c);
/* a complex, also providing a function name for errors */
/* Possible Python call: myfunction(1+2j) */
}
1.8. Именованные параметры для функций расширения
Функция PyArg_ParseTupleAndKeywords() объявлена следующим образом:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
const char *format, char * const *kwlist, ...);
Параметры arg и format идентичны параметрам функции PyArg_ParseTuple(). Параметр kwdict — это словарь именованных аргументов, полученный от среды выполнения Python в качестве третьего параметра. Параметр kwlist — это список строк, завершающийся NULL, которые задают имена параметров; имена сопоставляются с информацией о типах из format слева направо. В случае успеха PyArg_ParseTupleAndKeywords() возвращает истинное значение, в противном случае возвращает ложное значение и вызывает соответствующее исключение.
Примечание
Вложенные кортежи нельзя разбирать при использовании именованных аргументов! Передача именованных параметров, отсутствующих в kwlist, приведёт к вызову исключения TypeError.
Вот пример модуля, использующего именованные аргументы; он основан на примере Джеффа Филбрика (philbrick@hks.com):
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdarg_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "keywdarg",
.m_size = 0,
.m_methods = keywdarg_methods,
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModuleDef_Init(&keywdarg_module);
}
1.9. Создание произвольных значений
Эта функция является аналогом PyArg_ParseTuple(). Она объявлена следующим образом:
PyObject *Py_BuildValue(const char *format, ...);
Она распознаёт набор единиц формата, похожий на используемый функцией PyArg_ParseTuple(), но аргументы (которые передаются функции на вход, а не возвращаются на выходе) должны быть не указателями, а просто значениями. Функция возвращает новый объект Python, подходящий для возврата из функции C, вызванной из Python.
Есть одно отличие от PyArg_ParseTuple(): последняя требует, чтобы её первый аргумент был кортежем (поскольку списки аргументов Python внутри всегда представляются кортежами), тогда как Py_BuildValue() не всегда создаёт кортеж. Кортеж создаётся, только если строка формата содержит две или более единиц формата. Если строка формата пуста, функция возвращает None; если она содержит ровно одну единицу формата, функция возвращает объект, описанный этой единицей формата. Чтобы принудительно получить кортеж размера 0 или 1, заключите строку формата в скобки.
Примеры (слева — вызов, справа — результирующее значение Python):
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
1.10. Счётчики ссылок
В таких языках, как C или C++, программист отвечает за динамическое выделение и освобождение памяти в куче. В C для этого используются функции malloc() и free(). В C++ операторы new и delete используются практически в том же смысле; далее мы ограничимся рассмотрением языка C.
Каждый блок памяти, выделенный с помощью malloc(), в конечном счёте должен быть возвращён в пул доступной памяти ровно одним вызовом free(). Важно вызвать free() в нужный момент. Если адрес блока забыт, но для него не был вызван free(), занятую им память нельзя будет использовать повторно до завершения программы. Это называется утечкой памяти. С другой стороны, если программа вызывает free() для блока, а затем продолжает использовать этот блок, возникает конфликт с повторным использованием блока посредством другого вызова malloc(). Это называется использованием освобождённой памяти. Последствия столь же неприятны, как и при обращении к неинициализированным данным: дампы памяти, неправильные результаты, загадочные сбои.
Частая причина утечек памяти — необычные пути выполнения кода. Например, функция может выделить блок памяти, выполнить вычисления, а затем освободить этот блок. Изменение требований к функции может добавить проверку в вычисления, которая обнаруживает ошибочное состояние и приводит к преждевременному выходу из функции. При таком преждевременном выходе легко забыть освободить выделенный блок памяти, особенно если проверка была добавлена позднее. После возникновения такие утечки часто долго остаются незамеченными: выход по ошибке происходит лишь в небольшой доле всех вызовов, а в большинстве современных машин достаточно виртуальной памяти, поэтому утечка становится заметной только в длительно работающем процессе, часто использующем функцию с утечкой. Поэтому важно предотвращать утечки, используя соглашения или стратегии программирования, которые сводят к минимуму вероятность таких ошибок.
Поскольку Python активно использует malloc() и free(), ему также нужна стратегия предотвращения утечек памяти и использования освобождённой памяти. Выбранный метод называется подсчётом ссылок. Принцип прост: каждый объект содержит счётчик, который увеличивается при сохранении ссылки на объект и уменьшается при удалении ссылки на него. Когда счётчик достигает нуля, последняя ссылка на объект удалена, и объект освобождается.
Альтернативная стратегия называется автоматической сборкой мусора. (Иногда подсчёт ссылок также называют стратегией сборки мусора, поэтому я использую слово «автоматическая», чтобы различать эти два подхода.) Главное преимущество автоматической сборки мусора в том, что пользователю не нужно явно вызывать free(). (Также утверждается, что она повышает скорость или снижает использование памяти, однако это не доказанный факт.) Недостаток состоит в том, что для C не существует действительно переносимого автоматического сборщика мусора, тогда как подсчёт ссылок можно реализовать переносимым способом (при условии наличия функций malloc() и free(), наличие которых гарантируется стандартом C). Возможно, когда-нибудь для C появится достаточно переносимый автоматический сборщик мусора. До тех пор нам придётся обходиться подсчётом ссылок.
Хотя Python использует традиционную реализацию подсчёта ссылок, он также предоставляет детектор циклов для обнаружения циклических ссылок. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок — именно они являются слабым местом сборки мусора, основанной только на подсчёте ссылок. Циклические ссылки возникают, когда объекты содержат (возможно, косвенные) ссылки на самих себя, так что счётчик ссылок каждого объекта в цикле ненулевой. Типичные реализации подсчёта ссылок не могут освободить память, принадлежащую объектам в цикле ссылок или на которые ссылаются объекты этого цикла, даже если на сам цикл больше нет ссылок.
Детектор циклов способен обнаруживать циклы мусора и освобождать их. Модуль gc предоставляет возможность запустить детектор (функция collect()), а также интерфейсы настройки и возможность отключить детектор во время выполнения.
1.10.1. Подсчёт ссылок в Python
Для увеличения и уменьшения счётчика ссылок используются два макроса: Py_INCREF(x) и Py_DECREF(x). Макрос Py_DECREF() также освобождает объект, когда счётчик достигает нуля. Для гибкости он не вызывает free() напрямую, а обращается к функции через указатель в объекте типа. Для этой и других целей каждый объект также содержит указатель на свой объект типа.
Теперь остаётся главный вопрос: когда использовать Py_INCREF(x) и Py_DECREF(x)? Сначала введём несколько терминов. Никто не «владеет» объектом, однако можно владеть ссылкой на объект. Теперь счётчик ссылок объекта определяется как число принадлежащих кому-либо ссылок на него. Владелец ссылки отвечает за вызов Py_DECREF(), когда ссылка больше не нужна. Владение ссылкой можно передать. Существует три способа избавиться от принадлежащей вам ссылки: передать её дальше, сохранить или вызвать Py_DECREF(). Если забыть избавиться от принадлежащей вам ссылки, возникнет утечка памяти.
Также можно заимствовать [2] ссылку на объект. Тот, кто заимствует ссылку, не должен вызывать Py_DECREF(). Заимствованная ссылка не должна сохраняться дольше, чем ссылка владельца, у которого она была заимствована. Использование заимствованной ссылки после того, как владелец от неё избавился, чревато использованием освобождённой памяти; этого следует полностью избегать [3].
Преимущество заимствования перед владением ссылкой состоит в том, что вам не нужно заботиться об освобождении ссылки на всех возможных путях выполнения кода — иными словами, при использовании заимствованной ссылки нет риска утечки при преждевременном выходе. Недостаток заимствования заключается в том, что в некоторых тонких ситуациях заимствованная ссылка может быть использована в коде, который кажется корректным, уже после того, как владелец, у которого её заимствовали, фактически освободил её.
Заимствованную ссылку можно преобразовать в принадлежащую вам, вызвав Py_INCREF(). Это не влияет на статус владельца, у которого ссылка была заимствована: создаётся новая принадлежащая вам ссылка, и вы получаете все обязанности владельца (новый владелец должен правильно освободить ссылку, как и предыдущий владелец).
1.10.2. Правила владения
Каждый раз, когда ссылка на объект передаётся в функцию или из неё, в спецификации интерфейса функции должно быть указано, передаётся ли вместе со ссылкой владение.
Большинство функций, возвращающих ссылку на объект, передают вместе с ней владение. В частности, все функции, предназначенные для создания нового объекта, например PyLong_FromLong() и Py_BuildValue(), передают владение получателю. Даже если объект на самом деле не новый, вы всё равно получаете владение новой ссылкой на этот объект. Например, PyLong_FromLong() поддерживает кэш часто используемых значений и может вернуть ссылку на элемент из кэша.
Многие функции, извлекающие объекты из других объектов, также передают владение вместе со ссылкой; например, PyObject_GetAttrString(). Однако здесь картина менее однозначна, поскольку некоторые распространённые функции являются исключениями: PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem() и PyDict_GetItemString() возвращают ссылки, заимствованные у кортежа, списка или словаря.
Функция PyImport_AddModule() также возвращает заимствованную ссылку, хотя на самом деле может создать возвращаемый объект: это возможно, потому что принадлежащая ей ссылка на объект хранится в sys.modules.
При передаче ссылки на объект другой функции эта функция, как правило, заимствует её у вас; если ей потребуется сохранить ссылку, она вызовет Py_INCREF(), чтобы стать независимым владельцем. Из этого правила есть ровно два важных исключения: PyTuple_SetItem() и PyList_SetItem(). Эти функции принимают владение переданным им элементом — даже в случае ошибки! (Обратите внимание, что PyDict_SetItem() и подобные функции владение не принимают — они работают «обычным» образом.)
Когда функция C вызывается из Python, она заимствует ссылки на свои аргументы у вызывающего кода. Вызывающий код владеет ссылкой на объект, поэтому срок жизни заимствованной ссылки гарантирован до возврата из функции. Если такую заимствованную ссылку необходимо сохранить или передать дальше, её нужно преобразовать в принадлежащую вам ссылку, вызвав Py_INCREF().
Ссылка на объект, возвращаемая функцией C, вызванной из Python, должна принадлежать вызываемой функции — владение передаётся от функции вызывающему коду.
1.10.3. На тонком льду
Есть несколько ситуаций, в которых, казалось бы, безобидное использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут привести к освобождению ссылки её владельцем.
Первый и наиболее важный случай, о котором следует знать, — вызов Py_DECREF() для постороннего объекта при заимствовании ссылки на элемент списка. Например:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0); /* BUG! */
}
Сначала эта функция заимствует ссылку на list[0], затем заменяет list[1] значением 0 и, наконец, выводит заимствованную ссылку. Выглядит безобидно, верно? Но это не так!
Проследим ход выполнения внутри PyList_SetItem(). Список владеет ссылками на все свои элементы, поэтому при замене элемента 1 он должен освободить исходный элемент 1. Предположим, что исходный элемент 1 был экземпляром класса, определённого пользователем, и что в этом классе был определён метод __del__(). Если счётчик ссылок этого экземпляра класса равен 1, его освобождение вызовет метод __del__(). Внутри PyList_SetItem() вызывает Py_DECREF() для заменяемого элемента, что вызывает соответствующую функцию tp_dealloc заменяемого элемента. Во время освобождения tp_dealloc вызывает tp_finalize, который для экземпляров классов сопоставляется с методом __del__() (см. PEP 442). Вся эта последовательность выполняется синхронно во время вызова PyList_SetItem().
Поскольку метод __del__() написан на Python, он может выполнять произвольный код Python. Может ли он сделать что-нибудь, чтобы сделать недействительной ссылку на item в bug()? Ещё бы! Если список, переданный в bug(), доступен методу __del__(), тот может выполнить инструкцию вида del list[0]; если это была последняя ссылка на объект, связанная с ним память будет освобождена, что сделает item недействительной.
Как только источник проблемы известен, решение простое: временно увеличьте счётчик ссылок. Правильная версия функции выглядит так:
void
no_bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_INCREF(item);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
Py_DECREF(item);
}
Это реальная история. В одной из старых версий Python были варианты этой ошибки, и кто-то потратил немало времени, разбираясь в отладчике C, почему его методы __del__() не работают…
Второй случай проблем с заимствованной ссылкой — вариант, связанный с потоками. Обычно несколько потоков в интерпретаторе Python не мешают друг другу, поскольку всё пространство объектов Python защищено глобальной блокировкой. Однако эту блокировку можно временно освободить с помощью макроса Py_BEGIN_ALLOW_THREADS, а затем снова захватить с помощью Py_END_ALLOW_THREADS. Это часто делается вокруг блокирующих вызовов ввода-вывода, чтобы другие потоки могли использовать процессор, пока ожидается завершение операции ввода-вывода. Очевидно, что у следующей функции та же проблема, что и у предыдущей:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_BEGIN_ALLOW_THREADS
...some blocking I/O call...
Py_END_ALLOW_THREADS
PyObject_Print(item, stdout, 0); /* BUG! */
}
1.10.4. Нулевые указатели
Как правило, функции, принимающие в качестве аргументов ссылки на объекты, не предполагают, что вы передадите им указатели NULL, и при этом аварийно завершатся (или приведут к последующим аварийным завершениям). Функции, возвращающие ссылки на объекты, обычно возвращают NULL только для обозначения возникновения исключения. Причина, по которой аргументы NULL не проверяются, заключается в том, что функции часто передают полученные объекты другим функциям. Если бы каждая функция проверяла наличие NULL, возникло бы множество избыточных проверок, а код выполнялся бы медленнее.
Лучше проверять наличие NULL только в «источнике» — там, где получен указатель, который может быть NULL, например из malloc() или из функции, способной вызвать исключение.
Макросы Py_INCREF() и Py_DECREF() не проверяют, являются ли указатели NULL; однако их варианты Py_XINCREF() и Py_XDECREF() это делают.
Макросы для проверки типа конкретного объекта (Pytype_Check()) не проверяют, являются ли указатели NULL. И здесь причина та же: часто несколько таких макросов подряд проверяют объект на соответствие различным ожидаемым типам, и проверка породила бы избыточные операции. Вариантов с проверкой NULL не существует.
Механизм вызова функций C гарантирует, что список аргументов, передаваемый функциям C (args в примерах), никогда не бывает NULL; более того, гарантируется, что это всегда кортеж [4].
Передача указателя NULL пользователю Python — серьёзная ошибка.
1.11. Написание расширений на C++
Модули расширения можно писать на C++. При этом действуют некоторые ограничения. Если основная программа (интерпретатор Python) скомпилирована и скомпонована компилятором C, нельзя использовать глобальные или статические объекты с конструкторами. Это не представляет проблемы, если основная программа скомпонована компилятором C++. Функции, вызываемые интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с использованием extern "C". Заключать заголовочные файлы Python в extern "C" {...} не нужно — в них эта форма уже используется, если определён символ __cplusplus (все современные компиляторы C++ определяют этот символ).
1.12. Предоставление C API для модуля расширения
Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код модуля расширения может быть полезен и другим модулям расширения. Например, модуль расширения может реализовать тип «collection», работающий подобно спискам, но без порядка элементов. Подобно тому как стандартный тип списка Python имеет C API, позволяющий модулям расширения создавать списки и работать с ними, этот новый тип коллекции должен иметь набор функций C для непосредственной работы с ним из других модулей расширения.
На первый взгляд это кажется простым: достаточно написать функции (разумеется, не объявляя их с static), предоставить подходящий заголовочный файл и задокументировать C API. И действительно, это сработало бы, если бы все модули расширения всегда статически компоновались с интерпретатором Python. Однако, когда модули используются в виде разделяемых библиотек, символы, определённые в одном модуле, могут быть недоступны другому модулю. Особенности видимости зависят от операционной системы: в некоторых системах для интерпретатора Python и всех модулей расширения используется одно глобальное пространство имён (например, в Windows), тогда как в других требуется явный список импортируемых символов во время компоновки модуля (например, в AIX) или предлагается выбор различных стратегий (в большинстве Unix-подобных систем). И даже если символы глобально видимы, модуль с нужными функциями мог ещё не загрузиться!
Поэтому для переносимости нельзя делать никаких предположений о видимости символов. Это означает, что все символы в модулях расширения должны объявляться с static, за исключением функции инициализации модуля, чтобы избежать конфликтов имён с другими модулями расширения (как обсуждается в разделе Таблица методов модуля и функция инициализации). Кроме того, символы, которые должны быть доступны из других модулей расширения, необходимо экспортировать другим способом.
Python предоставляет специальный механизм для передачи информации уровня C (указателей) из одного модуля расширения в другой: Capsules. Capsule — это тип данных Python, в котором хранится указатель (void*). Создавать Capsule и обращаться к ним можно только через их C API, но их можно передавать, как и любые другие объекты Python. В частности, их можно присвоить имени в пространстве имён модуля расширения. Другие модули расширения могут импортировать этот модуль, получить значение по этому имени, а затем извлечь указатель из Capsule.
Есть много способов использовать Capsule для экспорта C API модуля расширения. Для каждой функции можно создать отдельную Capsule или хранить все указатели C API в массиве, адрес которого публикуется в Capsule. Различные задачи по сохранению и извлечению указателей можно по-разному распределить между модулем, предоставляющим код, и клиентскими модулями.
Какой бы способ вы ни выбрали, важно правильно задавать имена Capsule. Функция PyCapsule_New() принимает параметр name (const char*); можно передать имя NULL, но мы настоятельно рекомендуем указывать имя. Правильно названные Capsule обеспечивают определённую степень проверки типов во время выполнения: невозможно надёжно отличить одну Capsule без имени от другой.
В частности, Capsule, используемым для предоставления C API, следует задавать имена по следующему соглашению:
modulename.attributename
Вспомогательная функция PyCapsule_Import() упрощает загрузку C API, предоставляемого через Capsule, но только если имя Capsule соответствует этому соглашению. Благодаря такому поведению пользователи C API могут с высокой степенью уверенности полагаться на то, что загружаемая Capsule содержит нужный C API.
В следующем примере показан подход, при котором основная часть работы возлагается на автора экспортирующего модуля; этот подход подходит для широко используемых библиотечных модулей. Все указатели C API (в примере он всего один!) хранятся в массиве указателей void, который становится значением Capsule. В соответствующем модулю заголовочном файле определён макрос, отвечающий за импорт модуля и получение указателей C API; клиентским модулям нужно лишь вызвать этот макрос перед обращением к C API.
Экспортирующий модуль представляет собой изменённый модуль spam из раздела Простой пример. Функция spam.system() вызывает не напрямую библиотечную функцию C system(), а функцию PySpam_System(), которая, разумеется, в реальности выполняла бы более сложную задачу (например, добавляла бы «spam» к каждой команде). Эта функция PySpam_System() также экспортируется в другие модули расширения.
Функция PySpam_System() — обычная функция C, объявленная с static, как и всё остальное:
static int
PySpam_System(const char *command)
{
return system(command);
}
Функция spam_system() изменяется незначительным образом:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = PySpam_System(command);
return PyLong_FromLong(sts);
}
В начале модуля, сразу после строки
#include <Python.h>
нужно добавить ещё две строки:
#define SPAM_MODULE #include "spammodule.h"
Макрос #define используется, чтобы сообщить заголовочному файлу, что он включается в экспортирующий модуль, а не в клиентский. Наконец, функция mod_exec модуля должна инициализировать массив указателей C API:
static int
spam_module_exec(PyObject *m)
{
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
/* Initialize the C API pointer array */
PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;
/* Create a Capsule containing the API pointer array's address */
c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
return -1;
}
return 0;
}
Обратите внимание, что PySpam_API объявлен с static; иначе массив указателей исчезнет после завершения PyInit_spam()!
Основная часть работы выполняется в заголовочном файле spammodule.h, который выглядит так:
#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Header file for spammodule */
/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
/* Total number of C API pointers */
#define PySpam_API_pointers 1
#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
#else
/* This section is used in modules that use spammodule's API */
static void **PySpam_API;
#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])
/* Return -1 on error, 0 on success.
* PyCapsule_Import will set an exception if there's an error.
*/
static int
import_spam(void)
{
PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
return (PySpam_API != NULL) ? 0 : -1;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(Py_SPAMMODULE_H) */
Чтобы клиентский модуль получил доступ к функции PySpam_System(), ему достаточно вызвать функцию (точнее, макрос) import_spam() в своей функции mod_exec:
static int
client_module_exec(PyObject *m)
{
if (import_spam() < 0) {
return -1;
}
/* additional initialization can happen here */
return 0;
}
Главный недостаток этого подхода заключается в том, что файл spammodule.h довольно сложен. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому её нужно изучить только один раз.
Наконец, следует отметить, что Capsule предоставляют дополнительные возможности, особенно полезные для выделения и освобождения памяти, занятой указателем, хранящимся в Capsule. Подробности описаны в справочном руководстве по Python/C API в разделе Capsules и в реализации Capsule (файлы Include/pycapsule.h и Objects/pycapsule.c в дистрибутиве исходного кода Python).
Сноски
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/extending/extending.html