# 1. Розширення Python за допомогою C або C++

Доволі легко додати нові вбудовані модулі до Python, якщо ви знаєте, як програмувати на C. Такі extension modules можуть робити дві речі, які не можна зробити безпосередньо в Python: вони можуть реалізувати нові вбудовані модулі. -in типи об’єктів, і вони можуть викликати функції бібліотеки C і системні виклики.

Для підтримки розширень API Python (інтерфейс прикладного програмування) визначає набір функцій, макросів і змінних, які надають доступ до більшості аспектів системи виконання Python. API Python включено у вихідний файл C шляхом включення заголовка `"Python.h"`.

Компіляція модуля розширення залежить від його цільового використання, а також від налаштування вашої системи; подробиці наведено в наступних розділах.

Примітка

Інтерфейс розширення C є специфічним для CPython, а модулі розширення не працюють в інших реалізаціях Python. У багатьох випадках можна уникнути написання розширень C і зберегти переносимість на інші реалізації. Наприклад, якщо ви використовуєте виклик функцій бібліотеки C або системних викликів, вам слід розглянути можливість використання модуля `ctypes` або бібліотеки cffi замість написання спеціального коду C. Ці модулі дозволяють писати код Python для взаємодії з кодом C і є більш переносимими між реалізаціями Python, ніж написання та компіляція модуля розширення C.

## 1.1. Простий приклад

Давайте створимо модуль розширення під назвою `spam` (улюблена їжа шанувальників Monty Python…) і, скажімо, ми хочемо створити інтерфейс Python для функції бібліотеки C `system()` [1] . Ця функція приймає рядок символів із нулем у кінці як аргумент і повертає ціле число. Ми хочемо, щоб цю функцію можна було викликати з Python наступним чином:

    >>> import spam
    >>> status = spam.system("ls -l")

Почніть із створення файлу `spammodule.c`. (Історично склалося так, що якщо модуль називається `спам`, файл C, що містить його реалізацію, називається `spammodule.c`; якщо назва модуля дуже довга, наприклад `spammify`, назва модуля може бути просто `spammify.c`.)

Перші два рядки нашого файлу можуть бути:

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>

який залучає API Python (ви можете додати коментар із описом призначення модуля та повідомлення про авторські права, якщо хочете).

Примітка

Оскільки Python може визначати деякі визначення попереднього процесора, які впливають на стандартні заголовки в деяких системах, ви мусите включити `Python.h` перед тим, як включити будь-які стандартні заголовки.

`#define PY_SSIZE_T_CLEAN` was used to indicate that `Py_ssize_t` should be used in some APIs instead of `int`. It is not necessary since Python 3.13, but we keep it here for backward compatibility. See Рядки та буфери for a description of this macro.

All user-visible symbols defined by `Python.h` have a prefix of `Py` or `PY`, except those defined in standard header files.

Порада

For backward compatibility, `Python.h` includes several standard header files. C extensions should include the standard headers that they use, and should not rely on these implicit includes. If using the limited C API version 3.13 or newer, the implicit includes are:

- `<assert.h>`

- `<intrin.h>` (on Windows)

- `<inttypes.h>`

- `<limits.h>`

- `<math.h>`

- `<stdarg.h>`

- `<wchar.h>`

- `<sys/types.h>` (if present)

If `Py_LIMITED_API` is not defined, or is set to version 3.12 or older, the headers below are also included:

- `<ctype.h>`

- `<unistd.h>` (on POSIX)

If `Py_LIMITED_API` is not defined, or is set to version 3.10 or older, the headers below are also included:

- `<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()` повертає істину (не нуль), якщо всі аргументи мають правильний тип і його компоненти зберігаються в змінних, адреси яких передано. Він повертає false (нуль), якщо було передано недійсний список аргументів. В останньому випадку він також викликає відповідний виняток, щоб функція, що викликає, могла негайно повернути `NULL` (як ми бачили в прикладі).

## 1.2. Intermezzo: помилки та винятки

Важлива конвенція в інтерпретаторі 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()`, щоб перевірити, чи сталася помилка під час виклику функції, оскільки ви зможете визначити це за значенням, що повертається.

When a function f that calls another function g detects that the latter fails, f should itself return an error value (usually `NULL` or `-1`). It should not call one of the `PyErr_*` functions — one has already been called by g. f’s caller is then supposed to also return an error indication to its caller, again without calling `PyErr_*`, and so on — the most detailed cause of the error was already reported by the function that first detected it. Once the error reaches the Python interpreter’s main loop, this aborts the currently executing Python code and tries to find an exception handler specified by the Python programmer.

(There are situations where a module can actually give a more detailed error message by calling another `PyErr_*` function, and in such cases it is fine to do so. As a general rule, however, this is not necessary, and can cause information about the cause of the error to be lost: most operations can fail for a variety of reasons.)

Щоб проігнорувати виняток, встановлений невдалим викликом функції, умову виключення потрібно явно очистити, викликавши `PyErr_Clear()`. Єдиний раз, коли C-код повинен викликати `PyErr_Clear()`, це якщо він не хоче передавати помилку інтерпретатору, а хоче обробити її повністю сам (можливо, спробувавши щось інше або вдаючи, що нічого не пішло не так). ).

Кожен невдалий виклик `malloc()` має бути перетворений на виняток — прямий виклик `malloc()` (або `realloc()`) повинен викликати `PyErr_NoMemory()` і повертає сам індикатор помилки. Усі функції створення об’єктів (наприклад, `PyLong_FromLong()`) уже це роблять, тому ця примітка актуальна лише для тих, хто викликає `malloc()` безпосередньо.

Також зауважте, що, за винятком `PyArg_ParseTuple()` та друзів, функції, які повертають цілочисельний статус, зазвичай повертають додатне значення або нуль у разі успіху та `-1` у разі невдачі, як системні виклики Unix.

Нарешті, будьте обережні, щоб очистити сміття (за допомогою викликів `Py_XDECREF()` або `Py_DECREF()` для об’єктів, які ви вже створили), коли ви повертаєте індикатор помилки!

The choice of which exception to raise is entirely yours. There are predeclared C objects corresponding to all built-in Python exceptions, such as `PyExc_ZeroDivisionError`, which you can use directly. Of course, you should choose exceptions wisely — don’t use `PyExc_TypeError` to mean that a file couldn’t be opened (that should probably be `PyExc_OSError`). If something’s wrong with the argument list, the `PyArg_ParseTuple()` function usually raises `PyExc_TypeError`. If you have an argument whose value must be in a particular range or must satisfy other conditions, `PyExc_ValueError` is appropriate.

You can also define a new exception that is unique to your module. The simplest way to do this is to declare a static global object variable at the beginning of the file:

    static PyObject *SpamError = NULL;

and initialize it by calling `PyErr_NewException()` in the module’s `Py_mod_exec` function (`spam_module_exec()`):

    SpamError = PyErr_NewException("spam.error", NULL, NULL);

Since `SpamError` is a global variable, it will be overwritten every time the module is reinitialized, when the `Py_mod_exec` function is called.

For now, let’s avoid the issue: we will block repeated initialization by raising an `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);
    }

Note that the Python name for the exception object is `spam.error`. The `PyErr_NewException()` function may create a class with the base class being `Exception` (unless another class is passed in instead of `NULL`), described in Вбудовані винятки.

Note also that the `SpamError` variable retains a reference to the newly created exception class; this is intentional! Since the exception could be removed from the module by external code, an owned reference to the class is needed to ensure that it will not be discarded, causing `SpamError` to become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects.

For now, the `Py_DECREF()` call to remove this reference is missing. Even when the Python interpreter shuts down, the global `SpamError` variable will not be garbage-collected. It will «leak». We did, however, ensure that this will happen at most once per process.

We discuss the use of `PyMODINIT_FUNC` as a function return type later in this sample.

The `spam.error` exception can be raised in your extension module using a call to `PyErr_SetString()` as shown below:

    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;

It returns `NULL` (the error indicator for functions returning object pointers) if an error is detected in the argument list, relying on the exception set by `PyArg_ParseTuple()`. Otherwise the string value of the argument has been copied to the local variable `command`. This is a pointer assignment and you are not supposed to modify the string to which it points (so in Standard C, the variable `command` should properly be declared as `const char *command`).

Наступний оператор — це виклик функції Unix `system()`, передаючи їй рядок, який ми щойно отримали з `PyArg_ParseTuple()`:

    sts = system(command);

Our `spam.system()` function must return the value of `sts` as a Python object. This is done using the function `PyLong_FromLong()`.

    return PyLong_FromLong(sts);

У цьому випадку він поверне цілочисельний об’єкт. (Так, навіть цілі числа є об’єктами в купі в Python!)

If you have a C function that returns no useful argument (a function returning void), the corresponding Python function must return `None`. You need this idiom to do so (which is implemented by the `Py_RETURN_NONE` macro):

    Py_INCREF(Py_None);
    return Py_None;

`Py_None` — це ім’я C для спеціального об’єкта Python `None`. Це справжній об’єкт Python, а не покажчик `NULL`, що означає «помилку» в більшості контекстів, як ми бачили.

## 1.4. Таблиця методів модуля та функція ініціалізації

I promised to show how `spam_system()` is called from Python programs. First, we need to list its name and address in a «method table»:

    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()`; більше інформації про цю функцію наведено нижче.

The `METH_KEYWORDS` bit may be set in the third field if keyword arguments should be passed to the function. In this case, the C function should accept a third `PyObject *` parameter which will be a dictionary of keywords. Use `PyArg_ParseTupleAndKeywords()` to parse the arguments to such a function.

На таблицю методів має бути посилання в структурі визначення модуля:

    static struct PyModuleDef spam_module = {
        ...
        .m_methods = spam_methods,
        ...
    };

This structure, in turn, must be passed to the interpreter in the module’s initialization function. The initialization function must be named `PyInit_name()`, where name is the name of the module, and should be the only non-`static` item defined in the module file:

    PyMODINIT_FUNC
    PyInit_spam(void)
    {
        return PyModuleDef_Init(&spam_module);
    }

Note that `PyMODINIT_FUNC` declares the function as `PyObject *` return type, declares any special linkage declarations required by the platform, and for C++ declares the function as `extern "C"`.

`PyInit_spam()` is called when each interpreter imports its module `spam` for the first time. (See below for comments about embedding Python.) A pointer to the module definition must be returned via `PyModuleDef_Init()`, so that the import machinery can create the module and store it in `sys.modules`.

When embedding Python, the `PyInit_spam()` function is not called automatically unless there’s an entry in the `PyImport_Inittab` table. To add the module to the initialization table, use `PyImport_AppendInittab()`, optionally followed by an import of the module:

    #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);
    }

Примітка

If you declare a global variable or a local static one, the module may experience unintended side-effects on re-initialisation, for example when removing entries from `sys.modules` or importing compiled modules into multiple interpreters within a process (or following a `fork()` without an intervening `exec()`). If module state is not yet fully isolated, authors should consider marking the module as having no support for subinterpreters (via `Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED`).

A more substantial example module is included in the Python source distribution as `Modules/xxlimited.c`. This file may be used as a template or simply read as an example.

## 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;
    }

This function must be registered with the interpreter using the `METH_VARARGS` flag; this is described in section Таблиця методів модуля та функція ініціалізації. The `PyArg_ParseTuple()` function and its arguments are documented in section Вилучення параметрів у функціях розширення.

Макроси `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()` є «нейтральним щодо кількості посилань» щодо своїх аргументів. У прикладі було створено новий кортеж, який слугуватиме списком аргументів, який `Py_DECREF()`-редагується відразу після виклику `PyObject_CallObject()`.

Значення, що повертається `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 має бути рядком формату, синтаксис якого пояснюється в Розбір аргументів і створення значень у довідковому посібнику Python/C API. Решта аргументів мають бути адресами змінних, тип яких визначається рядком формату.

Зауважте, що хоча `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()` повертає true, інакше повертає false і викликає відповідний виняток.

Примітка

Вкладені кортежі неможливо проаналізувати за допомогою аргументів ключових слів! Передані параметри ключового слова, яких немає в 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 або одиницю, візьміть рядок формату в дужки.

Приклади (ліворуч виклик, праворуч результуюче значення 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.

Every block of memory allocated with `malloc()` should eventually be returned to the pool of available memory by exactly one call to `free()`. It is important to call `free()` at the right time. If a block’s address is forgotten but `free()` is not called for it, the memory it occupies cannot be reused until the program terminates. This is called a memory leak. On the other hand, if a program calls `free()` for a block and then continues to use the block, it creates a conflict with reuse of the block through another `malloc()` call. This is called using freed memory. It has the same bad consequences as referencing uninitialized data — core dumps, wrong results, mysterious crashes.

Поширеними причинами витоку пам’яті є незвичайні шляхи через код. Наприклад, функція може виділити блок пам’яті, виконати деякі обчислення, а потім знову звільнити блок. Тепер зміна вимог до функції може додати перевірку до обчислення, яка виявляє помилку та може передчасно повернутися з функції. Легко забути звільнити виділений блок пам’яті під час цього передчасного виходу, особливо коли він додається пізніше до коду. Такі витоки, щойно виникли, часто залишаються непоміченими протягом тривалого часу: вихід із помилкою відбувається лише в невеликій частині всіх викликів, а більшість сучасних машин мають багато віртуальної пам’яті, тому витік стає очевидним лише під час тривалого процесу який часто використовує функцію витоку. Тому важливо запобігти витокам, маючи угоду або стратегію кодування, яка мінімізує цей тип помилок.

Оскільки Python активно використовує `malloc()` і `free()`, йому потрібна стратегія, щоб уникнути витоку пам’яті, а також використання звільненої пам’яті. Обраний метод називається reference counting. Принцип простий: кожен об’єкт містить лічильник, який збільшується, коли десь зберігається посилання на об’єкт, і зменшується, коли посилання на нього видаляється. Коли лічильник досягає нуля, останнє посилання на об’єкт було видалено, а об’єкт звільнено.

Альтернативна стратегія називається автоматична збірка сміття <automatic garbage collection>. (Іноді підрахунок посилань також називають стратегією збирання сміття, тому я використовую «автоматичний», щоб відрізнити ці два.) Великою перевагою автоматичного збирання сміття є те, що користувачеві не потрібно викликати `free()` явно. (Іншою заявленою перевагою є покращення швидкості або використання пам’яті — однак це непереконливий факт.) Недоліком є те, що для C немає справді портативного автоматичного збирача сміття, тоді як підрахунок посилань можна реалізувати портативно (за умови, що функції `malloc()` і `free()` доступні — що гарантує стандарт C). Можливо, колись достатньо портативний автоматичний збирач сміття буде доступний для C. До того часу нам доведеться жити з кількістю посилань.

Хоча Python використовує традиційну реалізацію підрахунку посилань, він також пропонує детектор циклів, який працює для виявлення опорних циклів. Це дозволяє програмам не турбуватися про створення прямих чи непрямих циклічних посилань; це слабкі місця збирання сміття, реалізованого лише за допомогою підрахунку посилань. Посилальні цикли складаються з об’єктів, які містять (можливо, непрямі) посилання на себе, так що кожен об’єкт у циклі має кількість посилань, відмінну від нуля. Типові реалізації підрахунку посилань не можуть відновити пам’ять, що належить до будь-яких об’єктів у циклі посилань, або на яку посилаються об’єкти в циклі, навіть якщо немає подальших посилань на сам цикл.

Детектор циклів здатний виявляти цикли сміття та повертати їх. Модуль `gc` надає спосіб запуску детектора (функція `collect()`), а також інтерфейси налаштування та можливість вимкнути детектор під час виконання.

### 1.10.1. Підрахунок посилань у Python

Є два макроси, `Py_INCREF(x)` і `Py_DECREF(x)`, які обробляють збільшення та зменшення лічильника посилань. `Py_DECREF()` також звільняє об’єкт, коли кількість досягає нуля. З міркувань гнучкості він не викликає `free()` напряму — він робить виклик через вказівник на функцію в об’єкті type object. Для цієї мети (та інших) кожен об’єкт також містить покажчик на об’єкт свого типу.

Тепер залишається велике питання: коли використовувати `Py_INCREF(x)` і `Py_DECREF(x)`? Давайте спочатку введемо деякі терміни. Ніхто не «володіє» об’єктом; проте ви можете володіти посиланням <own a reference> на об’єкт. Кількість посилань на об’єкт тепер визначається як кількість належних посилань на нього. Власник посилання відповідає за виклик `Py_DECREF()`, коли посилання більше не потрібне. Право власності на посилання можна передати. Є три способи позбутися посилання, яке належить: передати, зберегти або викликати `Py_DECREF()`. Якщо забути позбутися посилання, що належить, це призводить до витоку пам’яті.

Також можна borrow [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` і, нарешті, друкує запозичене посилання. Виглядає нешкідливо, правда? Але це не так!

Let’s follow the control flow into `PyList_SetItem()`. The list owns references to all its items, so when item 1 is replaced, it has to dispose of the original item 1. Now let’s suppose the original item 1 was an instance of a user-defined class, and let’s further suppose that the class defined a `__del__()` method. If this class instance has a reference count of 1, disposing of it will call its `__del__()` method. Internally, `PyList_SetItem()` calls `Py_DECREF()` on the replaced item, which invokes replaced item’s corresponding `tp_dealloc` function. During deallocation, `tp_dealloc` calls `tp_finalize`, which is mapped to the `__del__()` method for class instances (see PEP 442). This entire sequence happens synchronously within the `PyList_SetItem()` call.

Since it is written in Python, the `__del__()` method can execute arbitrary Python code. Could it perhaps do something to invalidate the reference to `item` in `bug()`? You bet! Assuming that the list passed into `bug()` is accessible to the `__del__()` method, it could execute a statement to the effect of `del list[0]`, and assuming this was the last reference to that object, it would free the memory associated with it, thereby invalidating `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);
    }

This is a true story. An older version of Python contained variants of this bug and someone spent a considerable amount of time in a C debugger to figure out why his `__del__()` methods would fail…

The second case of problems with a borrowed reference is a variant involving threads. Normally, multiple threads in the Python interpreter can’t get in each other’s way, because there is a global lock protecting Python’s entire object space. However, it is possible to temporarily release this lock using the macro `Py_BEGIN_ALLOW_THREADS`, and to re-acquire it using `Py_END_ALLOW_THREADS`. This is common around blocking I/O calls, to let other threads use the processor while waiting for the I/O to complete. Obviously, the following function has the same problem as the previous one:

    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` лише на «джерело:», коли вказівник, який може бути `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, але іноді код у модулі розширення може бути корисним для інших модулів розширення. Наприклад, модуль розширення може реалізувати тип «колекція», який працює як списки без порядку. Подібно до того, як стандартний тип списку Python має C API, який дозволяє модулям розширення створювати списки та маніпулювати ними, цей новий тип колекції повинен мати набір функцій C для прямого маніпулювання з інших модулів розширення.

На перший погляд це здається легким: просто напишіть функції (звичайно, не оголошуючи їх «статичними»), надайте відповідний файл заголовка та задокументуйте C API. І насправді це працювало б, якби всі модулі розширення завжди були статично пов’язані з інтерпретатором Python. Проте коли модулі використовуються як спільні бібліотеки, символи, визначені в одному модулі, можуть бути невидимими для іншого модуля. Деталі видимості залежать від операційної системи; деякі системи використовують один глобальний простір імен для інтерпретатора Python і всіх модулів розширення (наприклад, Windows), тоді як інші вимагають явного списку імпортованих символів під час зв’язування модуля (прикладом є AIX) або пропонують вибір різних стратегій (більшість Unices). І навіть якщо символи видимі глобально, модуль, функції якого потрібно викликати, можливо, ще не завантажено!

Тому портативність вимагає не робити жодних припущень щодо видимості символу. Це означає, що всі символи в модулях розширення мають бути оголошені `статичними`, за винятком функції ініціалізації модуля, щоб уникнути зіткнення імен з іншими модулями розширення (як описано в розділі Таблиця методів модуля та функція ініціалізації). І це означає, що символи, які мають бути доступні з інших модулів розширення, повинні бути експортовані в інший спосіб.

Python provides a special mechanism to pass C-level information (pointers) from one extension module to another one: Capsules. A Capsule is a Python data type which stores a pointer (void*). Capsules can only be created and accessed via their C API, but they can be passed around like any other Python object. In particular, they can be assigned to a name in an extension module’s namespace. Other extension modules can then import this module, retrieve the value of this name, and then retrieve the pointer from the Capsule.

Є багато способів використання Capsules для експорту C API модуля розширення. Кожна функція може отримати власну капсулу, або всі покажчики C API можуть зберігатися в масиві, адреса якого опублікована в капсулі. Різні завдання зі зберігання та отримання покажчиків можуть бути розподілені різними способами між модулем, що надає код, і клієнтськими модулями.

Whichever method you choose, it’s important to name your Capsules properly. The function `PyCapsule_New()` takes a name parameter (const char*); you’re permitted to pass in a `NULL` name, but we strongly encourage you to specify a name. Properly named Capsules provide a degree of runtime type-safety; there is no feasible way to tell one unnamed Capsule from another.

Зокрема, капсулам, які використовуються для розкриття C API, слід присвоїти назву відповідно до цієї угоди:

    modulename.attributename

Зручна функція `PyCapsule_Import()` спрощує завантаження C API, що надається через Capsule, але лише якщо назва Capsule відповідає цій умові. Така поведінка дає користувачам C API високий ступінь впевненості, що капсула, яку вони завантажують, містить правильний C API.

The following example demonstrates an approach that puts most of the burden on the writer of the exporting module, which is appropriate for commonly used library modules. It stores all C API pointers (just one in the example!) in an array of void pointers which becomes the value of a Capsule. The header file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API.

The exporting module is a modification of the `spam` module from section Простий приклад. The function `spam.system()` does not call the C library function `system()` directly, but a function `PySpam_System()`, which would of course do something more complicated in reality (such as adding «spam» to every command). This function `PySpam_System()` is also exported to other extension modules.

The function `PySpam_System()` is a plain C function, declared `static` like everything else:

    static int
    PySpam_System(const char *command)
    {
        return system(command);
    }

The function `spam_system()` is modified in a trivial way:

    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"

The `#define` is used to tell the header file that it is being included in the exporting module, not a client module. Finally, the module’s `mod_exec` function must take care of initializing the C API pointer array:

    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;
    }

Note that `PySpam_API` is declared `static`; otherwise the pointer array would disappear when `PyInit_spam()` terminates!

Основна частина роботи знаходиться у файлі заголовка `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) */

All that a client module must do in order to have access to the function `PySpam_System()` is to call the function (or rather macro) `import_spam()` in its `mod_exec` function:

    static int
    client_module_exec(PyObject *m)
    {
        if (import_spam() < 0) {
            return -1;
        }
        /* additional initialization can happen here */
        return 0;
    }

Основним недоліком цього підходу є те, що файл `spammodule.h` досить складний. Однак базова структура однакова для кожної функції, яка експортується, тому її потрібно вивчати лише один раз.

Насамкінець слід зазначити, що капсули пропонують додаткову функціональність, яка особливо корисна для виділення пам’яті та зняття вказівника, що зберігається в капсулі. Подробиці описано в довідковому посібнику Python/C API у розділі Капсули і в реалізації капсул (файли `Include/pycapsule.h` і `Objects/pycapsule.c` у дистрибутиві вихідного коду Python).

Виноски

[1]

Інтерфейс для цієї функції вже існує в стандартному модулі `os` — він був обраний як простий і зрозумілий приклад.

[2]

Метафора «позичити» довідку не зовсім коректна: власник досі має копію довідки.

[3]

Перевірка того, що кількість посилань дорівнює принаймні 1, не працює — сама кількість посилань може бути у звільненій пам’яті та, таким чином, може бути повторно використана для іншого об’єкта!

[4]

Ці гарантії не діють, якщо ви використовуєте «старий» стиль викликів — він все ще міститься в більшості існуючих кодів.