FAQ extension/intégration

Puis-je créer mes propres fonctions en C ?

Oui, vous pouvez créer des modules intégrés contenant des fonctions, des variables, des exceptions et même de nouveaux types en C. Ceci est expliqué dans le document Extension et intégration de l'interpréteur Python.

La plupart des livres Python intermédiaires ou avancés couvrent également ce sujet.

Puis-je créer mes propres fonctions en C++ ?

Oui, en utilisant les fonctionnalités de compatibilité C existantes en C++. Placez extern "C" { ... } autour des fichiers Python inclus et mettez extern "C" avant chaque fonction qui va être appelée par l'interpréteur Python. Les objets C++ globaux ou statiques avec les constructeurs ne sont probablement pas une bonne idée.

Écrire directement en C est difficile ; existe-t-il des alternatives ?

Il y a un certain nombre de solutions existantes qui vous permettent d'écrire vos propres extensions C, selon ce que vous essayez de faire.

Cython and its relative Pyrex are compilers that accept a slightly modified form of Python and generate the corresponding C code. Cython and Pyrex make it possible to write an extension without having to learn Python's C API.

If you need to interface to some C or C++ library for which no Python extension currently exists, you can try wrapping the library's data types and functions with a tool such as SWIG. SIP, CXX Boost, or Weave are also alternatives for wrapping C++ libraries.

Comment puis-je exécuter des instructions quelconques Python à partir de C ?

La fonction de plus haut niveau pour ce faire est PyRun_SimpleStringString() qui prend une chaîne pour seul argument afin de l'exécuter dans le contexte du module __main__ et renvoie 0 en cas de succès et -1 quand une exception se produit (incluant SyntaxError). Pour une meilleure maîtrise, utilisez PyRun_String() ; voir le code source pour PyRun_SimpleString() dans Python/pythonrun.c.

Comment puis-je évaluer une expression quelconque de Python à partir de C ?

Appelez la fonction PyRun_String() de la question précédente avec le symbole de départ Py_eval_input ; il analyse une expression, l'évalue et renvoie sa valeur.

Comment puis-je extraire des donnés en C d'un objet Python ?

That depends on the object's type. If it's a tuple, PyTuple_Size() returns its length and PyTuple_GetItem() returns the item at a specified index. Lists have similar functions, PyList_Size() and PyList_GetItem().

For bytes, PyBytes_Size() returns its length and PyBytes_AsStringAndSize() provides a pointer to its value and its length. Note that Python bytes objects may contain null bytes so C's strlen() should not be used.

Pour tester le type d'un objet, assurez-vous d'abord qu'il ne soit pas NULL, puis utilisez PyBytes_Check(), PyTuple_Check(), PyList_Check(), etc.

Il y a aussi une API de haut niveau pour les objets Python qui est fournie par l'interface dite « abstraite » — voir Include/abstract.h pour plus de détails. Elle permet l'interfaçage avec tout type de séquence Python en utilisant des appels tels que PySequence_Length(), PySequence_GetItem(), etc. ainsi que de nombreux autres protocoles utiles tels que les nombres (PyNumber_Index() et autres) et les correspondances dans les APIs PyMapping.

Comment utiliser Py_BuildValue() pour créer un n-uplet de longueur définie ?

Vous ne pouvez pas. Utilisez PyTuple_Pack() à la place.

Comment puis-je appeler la méthode d'un objet à partir de C ?

La fonction PyObject_CallMethod() peut être utilisée pour appeler la méthode d'un objet. Les paramètres sont l'objet, le nom de la méthode à appeler, une chaîne de caractères comme celle utilisée pour Py_BuildValue() et les valeurs des arguments :

PyObject *
PyObject_CallMethod(PyObject *object, const char *method_name,
                    const char *arg_format, ...);

Cela fonctionne pour tous les objets qui ont des méthodes — qu'elles soient intégrées ou définies par l'utilisateur. Vous êtes responsable de « Py_DECREF()er » la valeur de retour à la fin.

Pour appeler, p. ex., la méthode seek d'un objet file avec les arguments 10, 0 (en supposant que le pointeur de l'objet fichier est f) :

res = PyObject_CallMethod(f, "seek", "(ii)", 10, 0);
if (res == NULL) {
        ... an exception occurred ...
}
else {
        Py_DECREF(res);
}

Notez que PyObject_CallObject() veut toujours un n-uplet comme liste d'arguments. Aussi, pour appeler une fonction sans arguments, utilisez "()" pour être conforme au type et, pour appeler une fonction avec un paramètre, entourez-le de parenthèses, p. ex. "(i)".

Comment puis-je récupérer la sortie de PyErr_Print() (ou tout ce qui s'affiche sur stdout/stderr) ?

Dans le code Python, définissez un objet qui possède la méthode write(). Affectez cet objet à sys.stdout et sys.stderr. Appelez print_error ou faites simplement en sorte que le mécanisme standard de remontée des erreurs fonctionne. Ensuite, la sortie sera dirigée vers l'endroit où votre méthode write() écrit.

La façon la plus simple consiste à utiliser la classe io.StringIO :

>>> import io, sys
>>> sys.stdout = io.StringIO()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(sys.stdout.getvalue())
foo
hello world!

Le code d'un objet à la fonctionnalité similaire ressemblerait à ceci :

>>> import io, sys
>>> class StdoutCatcher(io.TextIOBase):
...     def __init__(self):
...         self.data = []
...     def write(self, stuff):
...         self.data.append(stuff)
...
>>> import sys
>>> sys.stdout = StdoutCatcher()
>>> print('foo')
>>> print('hello world!')
>>> sys.stderr.write(''.join(sys.stdout.data))
foo
hello world!

Comment accéder à un module écrit en Python à partir de C ?

Vous pouvez obtenir un pointeur sur l'objet module comme suit :

module = PyImport_ImportModule("<modulename>");

Si le module n'a pas encore été importé (c.-à-d. qu'il n'est pas encore présent dans sys.modules), cela initialise le module ; sinon il renvoie simplement la valeur de sys.modules["<modulename>"]. Notez qu'il n'inscrit le module dans aucun espace de nommage — il s'assure seulement qu'il a été initialisé et qu'il est stocké dans sys.modules.

Vous pouvez alors accéder aux attributs du module (c.-à-d. à tout nom défini dans le module) comme suit :

attr = PyObject_GetAttrString(module, "<attrname>");

Appeler PyObject_SetAttrString() pour assigner des valeurs aux variables du module fonctionne également.

Comment s'interfacer avec les objets C++ depuis Python ?

Selon vos besoins, de nombreuses approches sont possibles. Pour le faire manuellement, commencez par lire le document "Extension et intégration". Sachez que pour le système d'exécution Python, il n'y a pas beaucoup de différence entre C et C++ — donc la méthode pour construire un nouveau type Python à partir d'une structure C (pointeur) fonctionne également avec des objets en C++.

Pour les bibliothèques C++, voir Écrire directement en C est difficile ; existe-t-il des alternatives ?.

J'ai ajouté un module en utilisant le fichier Setup et la compilation échoue ; pourquoi ?

Le fichier Setup doit se terminer par une ligne vide, s'il n'y a pas de ligne vide, le processus de compilation échoue (ce problème peut se régler en bidouillant un script shell, et ce bogue est si mineur qu'il ne mérite pas qu'on s'y attarde).

Comment déboguer une extension ?

Lorsque vous utilisez GDB avec des extensions chargées dynamiquement, vous ne pouvez pas placer de point d'arrêt dans votre extension tant que celle-ci n'est pas chargée.

Dans votre fichier .gdbinit (ou manuellement), ajoutez la commande :

br _PyImport_LoadDynamicModule

Ensuite, lorsque vous exécutez GDB :

$ gdb /local/bin/python
gdb) run myscript.py
gdb) continue # repeat until your extension is loaded
gdb) finish   # so that your extension is loaded
gdb) br myfunction.c:50
gdb) continue

Je veux compiler un module Python sur mon système Linux, mais il manque certains fichiers. Pourquoi ?

Most packaged versions of Python omit some files required for compiling Python extensions.

For Red Hat, install the python3-devel RPM to get the necessary files.

For Debian, run apt-get install python3-dev.

Comment distinguer une « entrée incomplète » (incomplete input) d'une « entrée invalide » (invalid input) ?

Parfois vous souhaitez émuler le comportement de l'interpréteur interactif Python, quand il vous donne une invite de continuation lorsque l'entrée est incomplète (par exemple, vous avez tapé le début d'une instruction "if" ou vous n'avez pas fermé vos parenthèses ou triple guillemets) mais il vous renvoie immédiatement une erreur syntaxique quand la saisie est incorrecte.

En Python, vous pouvez utiliser le module codeop, qui se rapproche assez du comportement de l'analyseur. Par exemple, IDLE l'utilise.

La façon la plus simple de le faire en C est d'appeler PyRun_InteractiveLoop() (peut-être dans un autre fil d'exécution) et laisser l'interpréteur Python gérer l'entrée pour vous. Vous pouvez également définir PyOS_ReadlineFunctionPointer() pour pointer vers votre fonction d'entrée personnalisée. Voir Modules/readline.c et Parser/myreadline.c pour plus de conseils.

Comment puis-je trouver les symboles g++ indéfinis __builtin_new ou __pure_virtual ?

Pour charger dynamiquement les modules d'extension g++, vous devez recompiler Python, effectuer l'édition de liens en utilisant g++ (modifiez LINKCC dans le Python Modules Makefile), et effectuer l'édition de liens de votre module d'extension avec g++ (par exemple, g++ -shared -o mymodule.so mymodule.o).

Puis-je créer une classe d'objets avec certaines méthodes implémentées en C et d'autres en Python (p. ex. en utilisant l'héritage) ?

Oui, vous pouvez hériter de classes intégrées telles que int, list, dict, etc.

The Boost Python Library (BPL, https://www.boost.org/libs/python/doc/index.html) provides a way of doing this from C++ (i.e. you can inherit from an extension class written in C++ using the BPL).