Dans ce tuto nous allons voir comment invoquer des fonctions codées en C dans un programme Python. Ces fonctions vont être associées à un module que nous nommerons « MyModule »
Voici le code Python qui importe ce module et invoque les fonction natives : notez bien que la seconde fonction déclenche une exception, d'où la présence du try
/ except
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#!/usr/bin/python3 import MyModule # Appel de la première fonction native print("You start with Python Code") print(MyModule.exemple("Hello")) # Appel de la fonction native levant une exception try: print(MyModule.exemple2()) except BaseException as e: print("Trop fort ce Python : " + str(e)) # Récupération des docstrings des fonctions print("Doc: " + MyModule.exemple.__doc__) print("Doc: " + MyModule.exemple2.__doc__) |
Il est maintenant temps d'implémenter le module en C. Le fichier de code suivant est découpé en deux parties. La première partie définie les méthodes exposées par le module : il y en a deux, appelées
exemple
et exemple2
. Notez aussi qu'une instance, correspondante à l'exception pouvant être levée par exemple2
, est définie dans cette première partie.
La seconde partie, correspond à la déclaration et l'initialisation de notre module natif : c'est une partie technique qui doit contenir les éléments suivants :
static PyMethodDef functions[]
: le tableau listant les fonctions à exposer en dehors du module. Il est déclaré static
car on ne veut pas qu'il soit manipulable à l'extérieur
du fichier de code C qui le définit. J'ai choisi de l'appeler functions
. Pour chaque entrèe du tableau, vous pouvez spécifier : le nom de la fonction coté Python, le pointeur sur la
function C associée, le type de la fonction et la documentation ratachée à cette fonction. Le tableau doit impérativement être terminé par une dernière entrée, qualifiée de « sentinelle »,
qui fixe ses valeurs à NULL
ou 0
: cela permettra de savoir quand le tableau se termine (pour rappel, en C, un tableau ne porte pas sa taille).
static struct PyModuleDef myModule
: une structure de définition du module. Elle comporte le nom du module du point de vue de Python, la documentation ratachée à ce module
(dans notre cas, il n'y en a pas), une configuration sur la gestion de l'état du module et le tableau de définition des fonctions exposés vu ci-dessus.
PyMODINIT_FUNC PyInit_MyModule(void)
: cette fonction est appelée lors de l'initialisation du module. Dans notre exemple, elle commence par créer le module, puis elle créé l'instance
d'exception utilisée par notre module et enfin elle doit renvoyer le module nouvellement créé.
Voici le code C de notre module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include <Python.h> /** Une fois importe, le module definit deux fonctions : - exemple qui prend une chaine en argument et retourne 0 - exemple2 qui leve une exception creee pour l'occasion **/ // --- Les fonctions exposées par votre module --- static PyObject* exempleException; static PyObject* exemple(PyObject* self, PyObject* args) { const char* chaine; if(!PyArg_ParseTuple(args, "s", &chaine)) return NULL; printf( "C/C++ code receive %s \n", chaine ); return PyLong_FromLong(0); } static PyObject* exemple2(PyObject* self, PyObject* args) { printf( "C/C++ code fire an exception\n" ); // Levée de l'exception PyErr_SetString(exempleException, "Exemple de levée d'erreur en C"); // Donc pas de retour particulier return NULL; } // --- Partie configuration du module --- // Tableau des fonctions exposées, avec la documentation. static PyMethodDef functions[] = { {"exemple", exemple, METH_VARARGS, "Une fonction"}, {"exemple2", exemple2, METH_VARARGS, "Une fonction levant une exception"}, {NULL, NULL, 0, NULL} }; // Données de définition du module static struct PyModuleDef myModule = { PyModuleDef_HEAD_INIT, "MyModule", /* nom du module */ NULL, /* documentation du module, ou NULL si non proposée */ -1, /* -1 => état du module stocké en global */ functions /* Tableau des fonctions exposées */ }; // Initialisation du module PyMODINIT_FUNC PyInit_MyModule(void) { // Création du module PyObject * module = PyModule_Create(&myModule); if (module == NULL) return NULL; // Création d'une instance d'exception exempleException = PyErr_NewException( "MyModule.error", NULL, NULL ); Py_INCREF(exempleException); PyModule_AddObject(module, "error", exempleException); // On doit renvoyer le module nouvellement créé return module; } |
Il faut maintenant compiler la partie de code native (en langage C). Et là j'imagine que vous commencez à paniquer. Il ne faut pas ! Trois étapes simples sont requises pour compiler notre module.
Installez un compilateur C sur votre machine. Sur Linux/Unix/Mac, je vous propose d'utiliser GCC : je vous laisse le soin de procéder à l'installation (si nécessaire) en utilisant le gestionnaire de paquets de votre système. Sous Windows, vous pouvez soit utiliser Visual Studio, soit utiliser MinGW-64 (un portage de GCC pour Windows) : à votre convenance.
Python fournit un outil de « build » pour vos modules natifs : le module distutils
. Il vous permet de vous affranchir du lancement du compilateur et de la connaissance des
options de compilation à fournir au compilateur :-). Pour l'utiliser, vous devrez fournir un module Python de compilation. Il est traditionnement appelé Setup.py
et contient notamment
la liste des fichiers C à compiler. En voici son contenu.
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/python3 from distutils.core import setup from distutils.core import Extension setup( name = 'MyModule', version = '1.0', ext_modules = [Extension('MyModule', ['MyModule.c']), ], ) |
Dernière étape, il faut lancez la compilation. Veuillez saisir la ligne de commande suivante : normalement, le module compilé doit être produit.
$> python3 Setup.py build_ext --inplace
Voici les résultats produits par notre exemple.
$> python3 UseModule.py You start with Python Code C/C++ code receive Hello 0 C/C++ code fire an exception Trop fort ce Python : Exemple de levée d'erreur en C Doc: Une fonction Doc: Une fonction levant une exception $>
|
|