# 1. Étendre Python en C ou C++

Il est relativement facile d'ajouter de nouveaux modules à Python, si vous savez programmer en C. Ces modules d'extension permettent deux choses qui ne sont pas possibles directement en Python : ils peuvent définir de nouveaux types natifs et peuvent appeler des fonctions de bibliothèques C ou faire des appels systèmes.

Pour gérer les extensions, l'API Python (Application Programmer Interface) définit un ensemble de fonctions, macros et variables qui donnent accès à la plupart des aspects du système d'exécution de Python. L'API Python est incorporée dans un fichier source C en incluant l'en-tête `Python.h`.

La compilation d'un module d'extension dépend de l'usage prévu et de la configuration du système, plus de détails peuvent être trouvés dans les chapitres suivants.

Note

l'interface d'extension C est spécifique à CPython, et les modules d'extension ne fonctionnent pas sur les autres implémentations de Python. Dans de nombreux cas, il est possible d'éviter la rédaction des extensions en C et ainsi préserver la portabilité vers d'autres implémentations. Par exemple, si vous devez appeler une fonction de la bibliothèque C ou faire un appel système, vous devriez envisager d'utiliser le module `ctypes` ou d'utiliser la bibliothèque cffi plutôt que d'écrire du code C sur mesure. Ces modules vous permettent d'écrire du code Python s'interfaçant avec le code C et sont plus portables entre les implémentations de Python que l'écriture et la compilation d'une d'extension C.

## 1.1. Un exemple simple

Créons un module d'extension appelé `spam` (la nourriture préférée de fans des Monty Python) et disons que nous voulons créer une interface Python à la fonction de la bibliothèque C `system()` [1]. Cette fonction prend une chaîne de caractères à terminaison nulle comme argument et renvoie un entier. Nous voulons que cette fonction soit appelable à partir de Python comme suit :

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

Commençons par créer un fichier `spammodule.c` (historiquement, si un module se nomme `spam`, le fichier C contenant son implémentation est appelé `spammodule.c` ; si le nom du module est très long, comme `spammify`, le nom du module peut être juste `spammify.c`).

Les deux premières lignes de notre fichier peuvent être :

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>

qui récupère l'API Python (vous pouvez ajouter un commentaire décrivant le but du module et un avis de droit d'auteur si vous le souhaitez).

Note

il est possible que Python déclare certaines définitions pré-processeur qui affectent les têtes standards sur certains systèmes, vous devez donc inclure `Python.h` avant les en-têtes standards.

Il est recommandé de toujours définir `PY_SSIZE_T_CLEAN` avant d'inclure `Python.h`. Lisez Extraire des paramètres dans des fonctions d'extension pour avoir une description de cette macro.

Tous les symboles exposés par `Python.h` sont préfixés de `Py` ou `PY`, sauf ceux qui sont définis dans les en-têtes standards. Pour le confort, et comme ils sont largement utilisés par l'interpréteur Python, `"Python.h"` inclut lui-même quelques en-têtes standards : `<stdio.h>`, `<string.h>`, `<errno.h>` et `<stdlib.h>`. Si ce dernier n'existe pas sur votre système, il déclare les fonctions `malloc()`, `free()` et `realloc()` directement.

La prochaine chose que nous ajoutons à notre fichier de module est la fonction C qui sera appelée lorsque l'expression Python `spam.system(chaîne)` sera évaluée (nous verrons bientôt comment elle finit par être appelée) :

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

Il y a une correspondance directe de la liste des arguments en Python (par exemple, l'expression `"ls -l"`) aux arguments passés à la fonction C. La fonction C a toujours deux arguments, appelés par convention self et args.

Pour les fonctions au niveau du module, l'argument self pointe sur l'objet module, pour une méthode, il pointe sur l'instance de l'objet.

L'argument args sera un pointeur vers un n-uplet Python contenant les arguments. Chaque élément du n-uplet correspond à un argument dans la liste des arguments de l'appel. Les arguments sont des objets Python, afin d'en faire quelque chose dans notre fonction C, nous devons les convertir en valeurs C. La fonction `PyArg_ParseTuple()` de l'API Python vérifie les types des arguments et les convertit en valeurs C. Elle utilise un modèle sous forme de chaîne pour déterminer les types requis des arguments ainsi que les types de variables C dans lequel stocker les valeurs converties. Nous approfondirons ceci plus tard.

`PyArg_ParseTuple()` renvoie vrai (pas zéro) si tous les arguments ont le bon type et que ses composants ont été stockés dans les variables dont les adresses ont été données en entrée. Elle renvoie faux (zéro) si une liste d'arguments invalide a été passée. Dans ce dernier cas, elle lève également une exception appropriée de sorte que la fonction d'appel puisse renvoyer `NULL` immédiatement (comme nous l'avons vu dans l'exemple).

## 1.2. Intermezzo : les erreurs et les exceptions

Une convention importante dans l'interpréteur Python est la suivante : lorsqu'une fonction échoue, elle doit définir une condition d'exception et renvoyer une valeur d'erreur (généralement `-1` ou un pointeur `NULL`). Les informations d'exception sont stockées dans trois attributs de l'état du thread de l'interpréteur. Ils valent `NULL` s'il n'y a pas d'exception. Sinon, ce sont les équivalents C des membres du n-uplet Python renvoyé par `sys.exc_info()`. Il s'agit du type d'exception, de l'instance d'exception et d'un objet de trace. Il est important de les connaître pour comprendre comment les erreurs sont transmises.

L'API Python définit un certain nombre de fonctions pour créer différents types d'exceptions.

La plus courante est `PyErr_SetString()`. Ses arguments sont un objet exception et une chaîne C. L'objet exception est généralement un objet prédéfini comme `PyExc_ZeroDivisionError`. La chaîne C indique la cause de l'erreur et est convertie en une chaîne Python puis stockée en tant que « valeur associée » à l'exception.

Une autre fonction utile est `PyErr_SetFromErrno()`, qui construit une exception à partir de la valeur de la variable globale `errno`. La fonction la plus générale est `PyErr_SetObject()`, qui prend deux arguments : l'exception et sa valeur associée. Vous ne devez pas appliquer `Py_INCREF()` aux objets transmis à ces fonctions.

Vous pouvez tester de manière non destructive si une exception a été levée avec `PyErr_Occurred()`. Cela renvoie l'objet exception actuel, ou `NULL` si aucune exception n'a eu lieu. Cependant, vous ne devriez pas avoir besoin d'appeler `PyErr_Occurred()` pour voir si une erreur est survenue durant l'appel d'une fonction, puisque vous devriez être en mesure de le déterminer à partir de la valeur renvoyée.

Lorsqu'une fonction f ayant appelé une autre fonction g détecte que cette dernière a échoué, f devrait donner une valeur d'erreur à son tour (habituellement `NULL` ou `-1`). La fonction f ne devrait pas appeler l'une des fonctions `PyErr_*`, l'une d'entre elles ayant déjà été appelée par g. La fonction appelant f est alors censée renvoyer aussi un code d'erreur à celle qui l'a appelée, toujours sans utiliser `PyErr_*`, et ainsi de suite. La cause la plus détaillée de l'erreur a déjà été signalée par la fonction l'ayant détecté en premier. Une fois l'erreur remontée à la boucle principale de l'interpréteur Python, il interrompt le code en cours d'exécution et essaie de trouver un gestionnaire d'exception spécifié par le développeur Python.

(Il y a des situations où un module peut effectivement donner un message d'erreur plus détaillé en appelant une autre fonction `PyErr_*` et, dans de tels cas, il est tout à fait possible de le faire. Cependant, ce n'est généralement pas nécessaire, et peut amener à perdre des informations sur la cause de l'erreur : la plupart des opérations peuvent échouer pour tout un tas de raisons.)

Pour ignorer une exception qui aurait été émise lors d'un appel de fonction qui a échoué, l'exception doit être retirée explicitement en appelant `PyErr_Clear()`. Le seul cas pour lequel du code C devrait appeler `PyErr_Clear()` est lorsqu'il ne veut pas passer l'erreur à l'interpréteur, mais souhaite la gérer lui-même (peut-être en essayant quelque chose d'autre, ou en prétendant que rien n'a mal tourné).

Chaque échec de `malloc()` doit être transformé en une exception, l'appelant direct de `malloc()` (ou `realloc()`) doit appeler `PyErr_NoMemory()` et prendre l'initiative de renvoyer une valeur d'erreur. Toutes les fonctions construisant des objets (tels que `PyLong_FromLong()`) le font déjà, donc cette note ne concerne que ceux qui appellent `malloc()` directement.

Notez également que, à l'exception notable de `PyArg_ParseTuple()` et compagnie, les fonctions qui renvoient leur statut sous forme d'entier donnent généralement une valeur positive ou zéro en cas de succès et `-1` en cas d'échec, comme les appels du système Unix.

Enfin, lorsque vous renvoyez un code d'erreur, n'oubliez pas faire un brin de nettoyage (en appelant `Py_XDECREF()` ou `Py_DECREF()` avec les objets que vous auriez déjà créés) !

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.

Vous pouvez également créer une exception spécifique à votre module. Pour cela, déclarez simplement une variable statique au début de votre fichier :

    static PyObject *SpamError;

and initialize it in your module's initialization function (`PyInit_spam()`) with an exception object:

    PyMODINIT_FUNC
    PyInit_spam(void)
    {
        PyObject *m;

        m = PyModule_Create(&spammodule);
        if (m == NULL)
            return NULL;

        SpamError = PyErr_NewException("spam.error", NULL, NULL);
        Py_XINCREF(SpamError);
        if (PyModule_AddObject(m, "error", SpamError) < 0) {
            Py_XDECREF(SpamError);
            Py_CLEAR(SpamError);
            Py_DECREF(m);
            return NULL;
        }

        return m;
    }

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 Exceptions natives.

Notez également que la variable `SpamError` contient une référence à la nouvelle classe créée ; ceci est intentionnel ! Comme l'exception peut être enlevée du module par du code externe, une référence à la classe est nécessaire pour assurer qu'elle ne sera pas supprimée par le ramasse-miettes, entraînant que `SpamError` devienne un pointeur dans le vide. Si cela se produisait, le code C qui lève cette exception peut engendrer un core dump ou des effets secondaires inattendus.

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. Retour vers l'exemple

En revenant vers notre fonction exemple, vous devriez maintenant être capable de comprendre cette affirmation :

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;

Elle renvoie `NULL` (l'indicateur d'erreur pour les fonctions renvoyant des pointeurs d'objet) si une erreur est détectée dans la liste des arguments, se fiant à l'exception définie par `PyArg_ParseTuple()`. Autrement, la valeur chaîne de l'argument a été copiée dans la variable locale `command`. Il s'agit d'une attribution de pointeur et vous n'êtes pas supposés modifier la chaîne vers laquelle il pointe (donc en C Standard, la variable `command` doit être clairement déclarée comme `const char *command`).

La prochaine instruction est un appel à la fonction Unix `system()`, en lui passant la chaîne que nous venons d'obtenir à partir de `PyArg_ParseTuple()` :

    sts = system(command);

Notre fonction `spam.system()` doit renvoyer la valeur de `sts` comme un objet Python. Cela est effectué par l'utilisation de la fonction `PyLong_FromLong()`.

    return PyLong_FromLong(sts);

Dans ce cas, elle renvoie un objet de type entier (oui, même les entiers sont des objets, stockés dans le tas, en Python !).

Si vous avez une fonction C qui ne renvoie aucun argument utile (une fonction renvoyant void), la fonction Python correspondante doit renvoyer `None`. Vous aurez besoin de cet idiome pour cela (qui est implémenté par la macro `Py_RETURN_NONE`) :

    Py_INCREF(Py_None);
    return Py_None;

`Py_None` est la dénomination en C pour l'objet spécial Python `None`. C'est un authentique objet Python plutôt qu'un pointeur `NULL` qui, dans la plupart des situations, signifie qu'une erreur est survenue comme nous l'avons vu.

## 1.4. La fonction d'initialisation et le tableau des méthodes du module

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 SpamMethods[] = {
        ...
        {"system",  spam_system, METH_VARARGS,
         "Execute a shell command."},
        ...
        {NULL, NULL, 0, NULL}        /* Sentinel */
    };

Notez la troisième entrée (`METH_VARARGS`). C'est un indicateur du type de convention à utiliser pour la fonction C, à destination de l'interpréteur. Il doit valoir normalement `METH_VARARGS` ou `METH_VARARGS | METH_KEYWORDS` ; la valeur `0` indique qu'une variante obsolète de `PyArg_ParseTuple()` est utilisée.

Si seulement `METH_VARARGS` est utilisé, la fonction s'attend à ce que les paramètres Python soient passés comme un n-uplet que l'on peut analyser via `PyArg_ParseTuple()` ; des informations supplémentaires sont fournies plus bas.

Le bit `METH_KEYWORDS` peut être mis à un dans le troisième champ si des arguments par mots-clés doivent être passés à la fonction. Dans ce cas, la fonction C doit accepter un troisième paramètre `PyObject *` qui est un dictionnaire des mots-clés. Utilisez `PyArg_ParseTupleAndKeywords()` pour analyser les arguments d'une telle fonction.

Le tableau des méthodes doit être référencé dans la structure de définition du module :

    static struct PyModuleDef spammodule = {
        PyModuleDef_HEAD_INIT,
        "spam",   /* name of module */
        spam_doc, /* module documentation, may be NULL */
        -1,       /* size of per-interpreter state of the module,
                     or -1 if the module keeps state in global variables. */
        SpamMethods
    };

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 PyModule_Create(&spammodule);
    }

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"`.

When the Python program imports module `spam` for the first time, `PyInit_spam()` is called. (See below for comments about embedding Python.) It calls `PyModule_Create()`, which returns a module object, and inserts built-in function objects into the newly created module based upon the table (an array of `PyMethodDef` structures) found in the module definition. `PyModule_Create()` returns a pointer to the module object that it creates. It may abort with a fatal error for certain errors, or return `NULL` if the module could not be initialized satisfactorily. The init function must return the module object to its caller, so that it then gets inserted into `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:

    int
    main(int argc, char *argv[])
    {
        wchar_t *program = Py_DecodeLocale(argv[0], NULL);
        if (program == NULL) {
            fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
            exit(1);
        }

        /* 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 */
        Py_SetProgramName(program);

        /* Initialize the Python interpreter.  Required.
           If this step fails, it will be a fatal error. */
        Py_Initialize();

        /* 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");
        }

        ...

        PyMem_RawFree(program);
        return 0;
    }

Note

supprimer des entrées de `sys.modules` ou importer des modules compilés dans plusieurs interpréteurs au sein d'un même processus (ou le faire à la suite d'un `fork()` sans un `exec()` préalable) peut créer des problèmes pour certains modules d'extension. Les auteurs de modules d'extension doivent faire preuve de prudence lorsqu'ils initialisent des structures de données internes.

Un exemple de module plus substantiel est inclus dans la distribution des sources Python sous le nom `Modules/xxmodule.c`. Ce fichier peut être utilisé comme modèle ou simplement lu comme exemple.

Note

contrairement à notre exemple de `spam`, `xxmodule` utilise une initialisation multi-phases (nouveau en Python 3.5), où une structure PyModuleDef est renvoyée à partir de `PyInit_spam`, et la création du module est laissée au mécanisme d'importation. Pour plus de détails sur l'initialisation multi-phases, voir PEP 489.

## 1.5. Compilation et liaison

Il y a encore deux choses à faire avant de pouvoir utiliser votre nouvelle extension : la compiler et la lier au système Python. Si vous utilisez le chargement dynamique, les détails peuvent dépendre du style de chargement dynamique utilisé par votre système ; voir les chapitres sur la compilation de modules d'extension (chapitre Construire des extensions C et C++) et les informations supplémentaires concernant uniquement la construction sous Windows (chapitre Construire des extensions C et C++ sur Windows) pour plus d'informations à ce sujet.

Si vous ne pouvez pas utiliser le chargement dynamique, ou si vous voulez faire de votre module une partie permanente de l'interpréteur Python, vous devez modifier la configuration et reconstruire l'interpréteur. Heureusement, c'est très simple sous Unix : placez simplement votre fichier (`spammodule.c` par exemple) dans le répertoire `Modules/` d'une distribution source décompressée, ajoutez une ligne au fichier `Modules/Setup.local` décrivant votre fichier :

    spam spammodule.o

et reconstruisez l'interpréteur en exécutant make dans le répertoire de niveau supérieur. Vous pouvez également exécuter make dans le sous-répertoire `Modules/`, mais vous devez d'abord reconstruire le `Makefile` en exécutant « make Makefile » (c'est nécessaire chaque fois que vous modifiez le fichier `Setup`).

Si votre module nécessite d'être lié à des bibliothèques supplémentaires, celles-ci peuvent être ajoutées à la fin de la ligne de votre module dans le fichier de configuration, par exemple :

    spam spammodule.o -lX11

## 1.6. Appeler des fonctions Python en C

Jusqu'à présent, nous nous sommes concentrés sur le fait de rendre les fonctions C appelables depuis Python. L'inverse est également utile : appeler des fonctions Python depuis C. C'est notamment le cas pour les bibliothèques qui gèrent les fonctions dites de « rappel » (callback en anglais). Si une interface C utilise des rappels, l'équivalent Python doit souvent fournir un mécanisme de rappel au développeur Python ; l'implémentation nécessite d'appeler les fonctions de rappel Python à partir d'un rappel C. D'autres utilisations sont également envisageables.

Heureusement, l'interpréteur Python est facilement appelé de manière récursive et il existe une interface standard pour appeler une fonction Python (nous ne nous attarderons pas sur la façon d'appeler l'analyseur Python avec une chaîne particulière en entrée — si vous êtes intéressé, jetez un œil à l'implémentation de l'option de ligne de commande `-c` dans `Modules/main.c` à partir du code source Python).

L'appel d'une fonction Python est facile. Tout d'abord, le programme Python doit vous transmettre d'une manière ou d'une autre l'objet de fonction Python. Vous devez fournir une fonction (ou une autre interface) pour ce faire. Lorsque cette fonction est appelée, enregistrez un pointeur vers l'objet de la fonction Python (faites attention à `Py_INCREF()` !) dans une variable globale — ou là où vous le souhaitez. Par exemple, la fonction suivante peut faire partie d'une définition de module :

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

Cette fonction doit être déclarée en utilisant le drapeau `METH_VARARGS` ; ceci est décrit dans la section La fonction d'initialisation et le tableau des méthodes du module. La fonction `PyArg_ParseTuple()` et ses arguments sont documentés dans la section Extraire des paramètres dans des fonctions d'extension.

Les macros `Py_XINCREF()` et `Py_XDECREF()` incrémentent/décrémentent le compteur des références d'un objet et sont sûres quant à la présence de pointeurs `NULL` (mais notez que temp ne sera pas `NULL` dans ce contexte). Plus d'informations à ce sujet dans la section Compteurs de références.

Plus tard, quand il est temps d'appeler la fonction, vous appelez la fonction C `PyObject_CallObject()`. Cette fonction requiert deux arguments, tous deux des pointeurs vers des objets Python arbitraires : la fonction Python et la liste d'arguments. La liste d'arguments doit toujours être un objet n-uplet, dont la longueur est le nombre d'arguments. Pour appeler la fonction Python sans argument, passez `NULL` ou le n-uplet vide ; pour l'appeler avec un argument, passez un n-uplet singleton. `Py_BuildValue()` renvoie un n-uplet lorsque sa chaîne de format se compose de zéro ou plusieurs codes de format entre parenthèses. Par exemple :

    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()` renvoie un pointeur d'objet Python : c'est la valeur de retour de la fonction Python. `PyObject_CallObject()` est « neutre en nombre de références » par rapport à ses arguments. Dans l'exemple, un nouveau n-uplet a été créé pour servir de liste d'arguments, qui est décrémenté (avec `Py_DECREF()`) immédiatement après l'appel `PyObject_CallObject()`.

La valeur de retour de `PyObject_CallObject()` est « nouvelle » : soit c'est un tout nouvel objet, soit c'est un objet existant dont le nombre de références a été incrémenté. Donc, à moins que vous ne vouliez l'enregistrer dans une variable globale, vous devriez en quelque sorte décrémenter avec `Py_DECREF()` le résultat, même (surtout !) si vous n'êtes pas intéressé par sa valeur.

Mais avant de le faire, il est important de vérifier que la valeur renvoyée n'est pas `NULL`. Si c'est le cas, la fonction Python s'est terminée par une levée d'exception. Si le code C qui a appelé `PyObject_CallObject()` est appelé depuis Python, il devrait maintenant renvoyer une indication d'erreur à son appelant Python, afin que l'interpréteur puisse afficher la pile d'appels, ou que le code Python appelant puisse gérer l'exception. Si cela n'est pas possible ou souhaitable, l'exception doit être effacée en appelant `PyErr_Clear()`. Par exemple :

    if (result == NULL)
        return NULL; /* Pass error back */
    ...use result...
    Py_DECREF(result);

Selon l'interface souhaitée pour la fonction de rappel Python, vous devrez peut-être aussi fournir une liste d'arguments à `PyObject_CallObject()`. Dans certains cas, la liste d'arguments est également fournie par le programme Python, par l'intermédiaire de la même interface qui a spécifié la fonction de rappel. Elle peut alors être sauvegardée et utilisée de la même manière que l'objet fonction. Dans d'autres cas, vous pouvez avoir à construire un nouveau n-uplet à passer comme liste d'arguments. La façon la plus simple de faire cela est d'appeler `Py_BuildValue()`. Par exemple, si vous voulez passer un code d'événement intégral, vous pouvez utiliser le code suivant :

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

Notez la présence de `Py_DECREF(arglist)` immédiatement après l'appel, avant la vérification des erreurs ! Notez également qu'à proprement parler, ce code n'est pas complet : `Py_BuildValue()` peut manquer de mémoire, et cela doit être vérifié.

Vous pouvez également appeler une fonction avec des arguments nommés en utilisant `PyObject_Call()`, qui accepte les arguments et les arguments nommés. Comme dans l'exemple ci-dessus, nous utilisons `Py_BuildValue()` pour construire le dictionnaire.

    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. Extraire des paramètres dans des fonctions d'extension

La fonction `PyArg_ParseTuple()` est déclarée ainsi :

    int PyArg_ParseTuple(PyObject *arg, const char *format, ...);

L'argument arg doit être un n-uplet contenant une liste d'arguments passée de Python à une fonction C. L'argument format doit être une chaîne de format, dont la syntaxe est expliquée dans Analyse des arguments et construction des valeurs dans le manuel de référence de l'API Python/C. Les arguments restants doivent être des adresses de variables dont le type est déterminé par la chaîne de format.

Notez que si `PyArg_ParseTuple()` vérifie que les arguments Python ont les types requis, elle ne peut pas vérifier la validité des adresses des variables C transmises à l'appel : si vous y faites des erreurs, votre code plantera probablement ou au moins écrasera des bits aléatoires en mémoire. Donc soyez prudent !

Notez que toute référence sur un objet Python donnée à l'appelant est une référence empruntée ; ne décrémentez pas son compteur de références !

Quelques exemples d'appels :

    #define PY_SSIZE_T_CLEAN  /* Make "s#" use Py_ssize_t rather than int. */
    #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. Paramètres nommés pour des fonctions d'extension

La fonction `PyArg_ParseTupleAndKeywords()` est déclarée ainsi :

    int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
                                    const char *format, char *kwlist[], ...);

Les paramètres arg et format sont identiques à ceux de la fonction `PyArg_ParseTuple()`. Le paramètre kwdict est le dictionnaire de mots-clés reçu comme troisième paramètre du runtime Python. Le paramètre kwlist est une liste de chaînes de caractères terminée par `NULL` qui identifie les paramètres ; les noms sont mis en correspondance, de gauche à droite, avec les informations de type de format. En cas de succès du processus, `PyArg_ParseTupleAndKeywords()` renvoie vrai, sinon elle renvoie faux et lève une exception appropriée.

Note

les n-uplets imbriqués ne peuvent pas être traités lorsqu'on utilise des arguments nommés ! Ceux-ci doivent apparaître dans kwlist, dans le cas contraire une exception `TypeError` est levée.

Voici un exemple de module qui utilise des arguments nommés, basé sur un exemple de Geoff Philbrick (philbrick@hks.com) :

    #define PY_SSIZE_T_CLEAN  /* Make "s#" use Py_ssize_t rather than int. */
    #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 keywdargmodule = {
        PyModuleDef_HEAD_INIT,
        "keywdarg",
        NULL,
        -1,
        keywdarg_methods
    };

    PyMODINIT_FUNC
    PyInit_keywdarg(void)
    {
        return PyModule_Create(&keywdargmodule);
    }

## 1.9. Créer des valeurs arbitraires

Cette fonction est le complément de `PyArg_ParseTuple()`. Elle est déclarée comme suit :

    PyObject *Py_BuildValue(const char *format, ...);

Elle reconnaît un ensemble d'unités de format similaires à celles reconnues par `PyArg_ParseTuple()`, mais les arguments (qui sont les données en entrée de la fonction, et non de la sortie) ne doivent pas être des pointeurs, mais juste des valeurs. Elle renvoie un nouvel objet Python, adapté pour être renvoyé par une fonction C appelée depuis Python.

Une différence avec `PyArg_ParseTuple()` : alors que cette dernière nécessite que son premier argument soit un n-uplet (puisque les listes d'arguments Python sont toujours représentées comme des n-uplets en interne), `Py_BuildValue()` ne construit pas toujours un n-uplet. Elle construit un n-uplet uniquement si sa chaîne de format contient deux unités de format ou plus. Si la chaîne de format est vide, elle renvoie `None` ; si elle contient exactement une unité de format, elle renvoie tout objet décrit par cette unité de format. Pour la forcer à renvoyer un n-uplet de taille 0 ou un, mettez la chaîne de format entre parenthèses.

Exemples (à gauche l'appel, à droite la valeur résultante, en 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. Compteurs de références

Dans les langages comme le C ou le C++, le développeur est responsable de l'allocation dynamique et de la dés-allocation de la mémoire sur le tas. En C, cela se fait à l'aide des fonctions `malloc()` et `free()`. En C++, les opérateurs `new` et `delete` sont utilisés avec essentiellement la même signification et nous limiterons la discussion suivante au cas du C.

Chaque bloc de mémoire alloué avec `malloc()` doit finalement être libéré par exactement un appel à `free()`. Il est important d'appeler `free()` au bon moment. Si l'adresse d'un bloc est oubliée mais que `free()` n'est pas appelée, la mémoire qu'il occupe ne peut être réutilisée tant que le programme n'est pas terminé. C'est ce qu'on appelle une fuite de mémoire. D'autre part, si un programme appelle `free()` pour un bloc et continue ensuite à utiliser le bloc, il crée un conflit avec la réutilisation du bloc via un autre appel `malloc()`. Cela s'appelle utiliser de la mémoire libérée. Cela a les mêmes conséquences néfastes que le référencement de données non initialisées — « core dumps », résultats erronés, plantages mystérieux.

Les causes courantes des fuites de mémoire sont des exécutions inhabituelles du code. Par exemple, une fonction peut allouer un bloc de mémoire, effectuer des calculs, puis libérer le bloc. Plus tard, une modification des exigences de la fonction peut ajouter un test au calcul qui détecte une condition d'erreur et peut sortir prématurément de la fonction. Il est facile d'oublier de libérer le bloc de mémoire alloué lors de cette sortie prématurée, surtout lorsqu'elle est ajoutée ultérieurement au code. De telles fuites, une fois introduites, passent souvent inaperçues pendant longtemps : la sortie d'erreur n'est prise que dans une petite fraction de tous les appels, et la plupart des machines modernes ont beaucoup de mémoire virtuelle, de sorte que la fuite ne devient apparente que dans un processus de longue durée qui utilise fréquemment cette fonction. Par conséquent, il est important d'éviter les fuites en ayant une convention ou une stratégie de codage qui minimise ce type d'erreurs.

Comme Python fait un usage intensif de `malloc()` et de `free()`, il a besoin d'une stratégie pour éviter les fuites de mémoire ainsi que l'utilisation de la mémoire libérée. La méthode choisie est appelée le comptage de références. Le principe est simple : chaque objet contient un compteur, qui est incrémenté lorsqu'une référence à l'objet est stockée quelque part, et qui est décrémenté lorsqu'une référence à celui-ci est supprimée. Lorsque le compteur atteint zéro, la dernière référence à l'objet a été supprimée et l'objet est libéré.

Une stratégie alternative est appelée ramasse-miettes automatique (automatic garbage collection en anglais). Parfois, le comptage des références est également appelé stratégie de ramasse-miettes, d'où l'utilisation du terme « automatique » pour distinguer les deux. Le grand avantage du ramasse-miettes est que l'utilisateur n'a pas besoin d'appeler `free()` explicitement (un autre avantage important est l'amélioration de la vitesse ou de l'utilisation de la mémoire, ce n'est cependant pas un fait avéré). L'inconvénient est que pour C, il n'y a pas de ramasse-miettes portable proprement-dit, alors que le comptage des références peut être implémenté de façon portable (tant que les fonctions `malloc()` et `free()` sont disponibles, ce que la norme C garantit). Peut-être qu'un jour un ramasse-miettes suffisamment portable sera disponible pour C. D'ici là, nous devons utiliser les compteurs de références.

Bien que Python utilise l'implémentation traditionnelle de comptage de références, il contient également un détecteur de cycles qui fonctionne pour détecter les cycles de références. Cela permet aux applications de ne pas se soucier de la création de références circulaires directes ou indirectes ; celles-ci sont les faiblesses des ramasse-miettes utilisant uniquement le comptage de références. Les cycles de références sont constitués d'objets qui contiennent des références (éventuellement indirectes) à eux-mêmes, de sorte que chaque objet du cycle a un compteur de références qui n'est pas nul. Les implémentations typiques de comptage de références ne sont pas capables de récupérer la mémoire des objets appartenant à un cycle de références, ou référencés à partir des objets dans le cycle, même s'il n'y a pas d'autre référence au cycle lui-même.

Le détecteur de cycle est capable de détecter les cycles isolés et peut récupérer la mémoire afférente. Le module `gc` expose un moyen d'exécuter le détecteur (la fonction `collect()`), ainsi que des interfaces de configuration et la possibilité de désactiver le détecteur au moment de l'exécution.

### 1.10.1. Comptage de références en Python

Il existe deux macros, `Py_INCREF(x)` et `Py_DECREF(x)`, qui gèrent l'incrémentation et la décrémentation du comptage de références. `Py_DECREF()` libère également l'objet lorsque le compteur atteint zéro. Pour plus de flexibilité, il n'appelle pas `free()` directement — plutôt, il fait un appel à travers un pointeur de fonction dans l'objet type objet de l'objet. À cette fin (et pour d'autres), chaque objet contient également un pointeur vers son objet type.

La grande question demeure maintenant : quand utiliser `Py_INCREF(x)` et `Py_DECREF(x)` ? Commençons par définir quelques termes. Personne ne possède un objet, mais vous pouvez en avoir une référence. Le compteur de références d'un objet est maintenant défini comme étant le nombre de références à cet objet. Le propriétaire d'une référence est responsable d'appeler `Py_DECREF()` lorsque la référence n'est plus nécessaire. La possession d'une référence peut être transférée. Il y a trois façons de se débarrasser d'une référence que l'on possède : la transmettre, la stocker ou appeler `Py_DECREF()`. Oublier de se débarrasser d'une référence crée une fuite de mémoire.

Il est également possible d'emprunter [2] une référence à un objet. L'emprunteur d'une référence ne doit pas appeler `Py_DECREF()`. L'emprunteur ne doit pas conserver l'objet plus longtemps que le propriétaire à qui il a été emprunté. L'utilisation d'une référence empruntée après que le propriétaire s'en est débarrassée risque d'utiliser de la mémoire libérée et doit être absolument évitée [3].

L'avantage d'emprunter, plutôt qu'être propriétaire d'une référence, est que vous n'avez pas à vous soucier de libérer la référence sur tous les chemins possibles dans le code — en d'autres termes, avec une référence empruntée, vous ne courez pas le risque de fuites lors d'une sortie prématurée. L'inconvénient de l'emprunt par rapport à la possession est qu'il existe certaines situations subtiles où, dans un code apparemment correct, une référence empruntée peut être utilisée après que le propriétaire auquel elle a été empruntée l'a en fait éliminée.

Une référence empruntée peut être changée en une référence possédée en appelant `Py_INCREF()`. Cela n'affecte pas le statut du propriétaire à qui la référence a été empruntée — cela crée une nouvelle référence possédée et donne l'entière responsabilité au propriétaire (le nouveau propriétaire doit libérer la référence correctement, ainsi que le propriétaire précédent).

### 1.10.2. Règles concernant la possession de références

Chaque fois qu'une référence d'objet est passée à l'intérieur ou à l'extérieur d'une fonction, elle fait partie de la spécification de l'interface de la fonction, peu importe que la possession soit transférée avec la référence ou non.

La plupart des fonctions qui renvoient une référence à un objet transmettent la possession avec la référence. En particulier, toutes les fonctions dont la fonction est de créer un nouvel objet, telles que `PyLong_FromLong()` et `Py_BuildValue()`, transmettent la possession au récepteur. Même si l'objet n'est pas réellement nouveau, vous recevez toujours la possession d'une nouvelle référence à cet objet. Par exemple, `PyLong_FromLong()` maintient un cache des valeurs souvent utilisées et peut renvoyer une référence à un élément mis en cache.

De nombreuses fonctions qui extraient des objets d'autres objets transfèrent également la possession avec la référence, par exemple `PyObject_GetAttrString()`. L'image est moins claire ici, cependant, puisque quelques routines courantes sont des exceptions : `PyTuple_GetItem()`, `PyList_GetItem()`, `PyDict_GetItem()` et `PyDict_GetItemString()` renvoient toutes des références qui sont empruntées au n-uplet, à la liste ou au dictionnaire.

La fonction `PyImport_AddModule()` renvoie également une référence empruntée, même si elle peut en fait créer l'objet qu'elle renvoie : c'est possible car une référence à l'objet est stockée dans `sys.modules`.

Lorsque vous passez une référence d'objet dans une autre fonction, en général, la fonction vous emprunte la référence — si elle a besoin de la stocker, elle utilise `Py_INCREF()` pour devenir un propriétaire indépendant. Il existe exactement deux exceptions importantes à cette règle : `PyTuple_SetItem()` et `PyList_SetItem()`. Ces fonctions s'approprient l'élément qui leur est transmis, même en cas d'échec ! (Notez que `PyDict_SetItem()` et compagnie ne prennent pas la possession de l'objet, elles sont « normales ».)

Lorsqu'une fonction C est appelée depuis Python, elle emprunte à l'appelant des références aux arguments. L'appelant possède une référence à l'objet, de sorte que la durée de vie de la référence empruntée est garantie jusqu'au retour de la fonction. Ce n'est que lorsqu'une telle référence empruntée doit être stockée ou transmise qu'elle doit être transformée en référence possédée en appelant `Py_INCREF()`.

La référence d'objet renvoyée par une fonction C appelée depuis Python doit être une référence détenue en propre — la possession est transférée de la fonction à son appelant.

### 1.10.3. Terrain dangereux

Il existe quelques situations où l'utilisation apparemment inoffensive d'une référence empruntée peut entraîner des problèmes. Tous ces problèmes sont en lien avec des invocations implicites de l’interpréteur et peuvent amener le propriétaire d'une référence à s'en défaire.

Le premier cas, et le plus important à connaître, est celui de l'utilisation de `Py_DECREF()` à un objet non relié lors de l'emprunt d'une référence à un élément de liste. Par exemple :

    void
    bug(PyObject *list)
    {
        PyObject *item = PyList_GetItem(list, 0);

        PyList_SetItem(list, 1, PyLong_FromLong(0L));
        PyObject_Print(item, stdout, 0); /* BUG! */
    }

Cette fonction emprunte d'abord une référence à `list[0]`, puis remplace `list[1]` par la valeur `0`, et enfin affiche la référence empruntée. Ça a l'air inoffensif, n'est-ce pas ? Mais ce n'est pas le cas !

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.

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`.

La solution, une fois que vous connaissez la source du problème, est simple : incrémentez temporairement le compteur de références. La version correcte de la fonction est :

    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...

Le deuxième cas de problèmes liés à une référence empruntée est une variante impliquant des fils d'exécution. Normalement, plusieurs threads dans l'interpréteur Python ne peuvent pas se gêner mutuellement, car il existe un verrou global protégeant tout l'espace objet de Python. Cependant, il est possible de libérer temporairement ce verrou en utilisant la macro `Py_BEGIN_ALLOW_THREADS`, et de le ré-acquérir en utilisant `Py_END_ALLOW_THREADS`. Ceci est un procédé courant pour bloquer les appels d'entrées/sorties, afin de permettre aux autres threads d'utiliser le processeur en attendant que les E/S soient terminées. Évidemment, la fonction suivante a le même problème que la précédente :

    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. Pointeurs `NULL`

En général, les fonctions qui prennent des références d'objets comme arguments ne sont pas conçues pour recevoir des pointeurs `NULL`, et si vous en donnez comme arguments, elles causeront une erreur de segmentation (ou provoqueront des core dump ultérieurs). Les fonctions qui renvoient des références d'objets renvoient généralement `NULL` uniquement pour indiquer qu'une exception s'est produite. La raison pour laquelle les arguments `NULL` ne sont pas testés est que les fonctions passent souvent les objets qu'elles reçoivent à d'autres fonctions, si chaque fonction devait tester pour `NULL`, il y aurait beaucoup de tests redondants et le code s'exécuterait plus lentement.

Il est préférable de tester la présence de `NULL` uniquement au début : lorsqu'un pointeur qui peut être `NULL` est reçu, par exemple, de `malloc()` ou d'une fonction qui peut lever une exception.

Les macros `Py_INCREF()` et `Py_DECREF()` ne vérifient pas les pointeurs `NULL`. Cependant, leurs variantes `Py_XINCREF()` et `Py_XDECREF()` le font.

Les macros de vérification d'un type d'objet particulier (`Pytype_Check()`) ne vérifient pas les pointeurs `NULL` — encore une fois, il y a beaucoup de code qui en appelle plusieurs à la suite pour tester un objet par rapport à différents types attendus, ce qui générerait des tests redondants. Il n'y a pas de variantes avec la vérification `NULL`.

Le mécanisme d'appel de fonctions C garantit que la liste d'arguments passée aux fonctions C (`args` dans les exemples) n'est jamais `NULL`. En fait, il garantit qu'il s'agit toujours d'un n-uplet [4].

C'est une grave erreur de laisser un pointeur `NULL` « s'échapper » vers l'utilisateur Python.

## 1.11. Écrire des extensions en C++

C'est possible d'écrire des modules d'extension en C++, mais sous certaines conditions. Si le programme principal (l'interpréteur Python) est compilé et lié par le compilateur C, les objets globaux ou statiques avec les constructeurs ne peuvent pas être utilisés. Ceci n'est pas un problème si le programme principal est relié par le compilateur C++. Les fonctions qui seront appelées par l'interpréteur Python (en particulier, les fonctions d'initialisation des modules) doivent être déclarées en utilisant `extern "C"`. Il n'est pas nécessaire d'inclure les fichiers d'en-tête Python dans le `extern "C" {…}`, car ils utilisent déjà ce format si le symbole `__cplusplus` est défini (tous les compilateurs C++ récents définissent ce symbole).

## 1.12. Fournir une API en langage C pour un module d'extension

De nombreux modules d'extension fournissent simplement de nouvelles fonctions et de nouveaux types à utiliser à partir de Python, mais parfois le code d'un module d'extension peut être utile pour d'autres modules d'extension. Par exemple, un module d'extension peut mettre en œuvre un type « collection » qui fonctionne comme des listes sans ordre. Tout comme le type de liste Python standard possède une API C qui permet aux modules d'extension de créer et de manipuler des listes, ce nouveau type de collection devrait posséder un ensemble de fonctions C pour une manipulation directe à partir d'autres modules d'extension.

À première vue, cela semble facile : il suffit d'écrire les fonctions (sans les déclarer « statiques », bien sûr), de fournir un fichier d'en-tête approprié et de documenter l'API C. Et en fait, cela fonctionnerait si tous les modules d'extension étaient toujours liés statiquement avec l'interpréteur Python. Cependant, lorsque les modules sont utilisés comme des bibliothèques partagées, les symboles définis dans un module peuvent ne pas être visibles par un autre module. Les détails de la visibilité dépendent du système d'exploitation ; certains systèmes utilisent un espace de noms global pour l'interpréteur Python et tous les modules d'extension (Windows, par exemple), tandis que d'autres exigent une liste explicite des symboles importés au moment de la liaison des modules (AIX en est un exemple), ou offrent un choix de stratégies différentes (la plupart des Unix). Et même si les symboles sont globalement visibles, le module dont on souhaite appeler les fonctions n'est peut-être pas encore chargé !

La portabilité exige donc de ne faire aucune supposition sur la visibilité des symboles. Cela signifie que tous les symboles des modules d'extension doivent être déclarés `static`, à l'exception de la fonction d'initialisation du module, afin d'éviter les conflits de noms avec les autres modules d'extension (comme discuté dans la section La fonction d'initialisation et le tableau des méthodes du module). Et cela signifie que les symboles qui devraient être accessibles à partir d'autres modules d'extension doivent être exportés d'une manière différente.

Python fournit un mécanisme spécial pour transmettre des informations de niveau C (pointeurs) d'un module d'extension à un autre : les Capsules. Une capsule est un type de données Python qui stocke un pointeur (void*). Les capsules ne peuvent être créées et accessibles que via leur API C, mais elles peuvent être transmises comme n'importe quel autre objet Python. En particulier, elles peuvent être affectées à un nom dans l'espace de noms d'un module d'extension. D'autres modules d'extension peuvent alors importer ce module, récupérer la valeur de ce nom, puis récupérer le pointeur de la Capsule.

Il existe de nombreuses façons d'utiliser les Capsules pour exporter l'API C d'un module d'extension. Chaque fonction peut obtenir sa propre Capsule, ou tous les pointeurs de l'API C peuvent être stockés dans un tableau dont l'adresse est inscrite dans une Capsule. Et les différentes tâches de stockage et de récupération des pointeurs peuvent être réparties de différentes manières entre le module fournissant le code et les modules clients.

Quelle que soit la méthode que vous choisissez, il est important de bien nommer vos Capsules. La fonction `PyCapsule_New()` prend un paramètre nommé (const char*). Vous êtes autorisé à passer un nom `NULL`, mais nous vous encourageons vivement à spécifier un nom. Des Capsules correctement nommées offrent un certain degré de sécurité concernant un éventuel conflit de types, car il n'y a pas de moyen de distinguer deux ou plusieurs Capsules non nommées entre elles.

En particulier, les capsules utilisées pour exposer les API C doivent recevoir un nom suivant cette convention :

    modulename.attributename

La fonction de commodité `PyCapsule_Import()` permet de charger facilement une API C fournie via une Capsule, mais seulement si le nom de la Capsule correspond à cette convention. Ce comportement donne aux utilisateurs d'API C un degré élevé de certitude que la Capsule qu'ils chargent contient l'API C correcte.

L'exemple suivant montre une approche qui fait peser la plus grande partie de la charge sur le rédacteur du module d'exportation, ce qui est approprié pour les modules de bibliothèque couramment utilisés. Il stocke tous les pointeurs de l'API C (un seul dans l'exemple !) dans un tableau de pointeurs void qui devient la valeur d'une Capsule. Le fichier d'en-tête correspondant au module fournit une macro qui se charge d'importer le module et de récupérer ses pointeurs d'API C. Les modules clients n'ont qu'à appeler cette macro avant d'accéder à l'API C.

The exporting module is a modification of the `spam` module from section Un exemple simple. 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);
    }

Au début du module, immédiatement après la ligne :

    #include <Python.h>

on doit ajouter deux lignes supplémentaires :

    #define SPAM_MODULE
    #include "spammodule.h"

L'indicateur `#define` est utilisé pour indiquer au fichier d'en-tête qu'il est inclus dans le module d'exportation, et non dans un module client. Enfin, la fonction d'initialisation du module doit prendre en charge l'initialisation du tableau de pointeurs de l'API C :

    PyMODINIT_FUNC
    PyInit_spam(void)
    {
        PyObject *m;
        static void *PySpam_API[PySpam_API_pointers];
        PyObject *c_api_object;

        m = PyModule_Create(&spammodule);
        if (m == NULL)
            return NULL;

        /* 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_AddObject(m, "_C_API", c_api_object) < 0) {
            Py_XDECREF(c_api_object);
            Py_DECREF(m);
            return NULL;
        }

        return m;
    }

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

L'essentiel du travail se trouve dans le fichier d'en-tête `spammodule.h`, qui ressemble à ceci :

    #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 initialization function:

    PyMODINIT_FUNC
    PyInit_client(void)
    {
        PyObject *m;

        m = PyModule_Create(&clientmodule);
        if (m == NULL)
            return NULL;
        if (import_spam() < 0)
            return NULL;
        /* additional initialization can happen here */
        return m;
    }

Le principal inconvénient de cette approche est que le fichier `spammodule.h` est assez compliqué. Cependant, la structure de base est la même pour chaque fonction exportée, ce qui fait qu'elle ne doit être apprise qu'une seule fois.

Enfin, il convient de mentionner que les Capsules offrent des fonctionnalités supplémentaires, qui sont particulièrement utiles pour l'allocation de la mémoire et la dés-allocation du pointeur stocké dans un objet Capsule. Les détails sont décrits dans le manuel de référence de l'API Python/C dans la section Capsules et dans l'implémentation des Capsules (fichiers `Include/pycapsule.h` et `Objects/pycapsule.c` dans la distribution du code source Python).

Notes

[1]

Une interface pour cette fonction existe déjà dans le module standard `os`, elle a été choisie comme un exemple simple et direct.

[2]

L'expression « emprunter une référence » n'est pas tout à fait correcte, car le propriétaire a toujours une copie de la référence.

[3]

Vérifier que le comptage de référence est d'au moins 1 ne fonctionne pas, le compte de référence lui-même pourrait être en mémoire libérée et peut donc être réutilisé pour un autre objet !

[4]

Ces garanties ne sont pas valables lorsqu'on emploie les conventions de nommage anciennes, qu'on retrouve encore assez souvent dans beaucoup de code existant.