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 et son cousin Pyrex sont des compilateurs qui acceptent une
forme légèrement modifiée de Python et produisent du code C
correspondant. Cython et Pyrex permettent d'écrire une extension sans
avoir à connaître l'API C de Python.

Si vous avez besoin d'accéder à l'interface d'une bibliothèque C ou
C++ pour laquelle aucune extension Python n'existe à ce jour, vous
pouvez essayer d'encapsuler les types de données et fonctions de la
bibliothèque avec un outil tel que SWIG. SIP, CXX, Boost ou Weave sont
également des alternatives pour encapsuler des bibliothèques C++.


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 ?
============================================================

Cela dépend du type d'objet. Si c'est un *n*-uplet, "PyTuple_Size()"
renvoie sa longueur et "PyTuple_GetItem()" renvoie l'élément à l'index
spécifié. Les listes ont des fonctions similaires, "PyListSize()" et
"PyList_GetItem()".

Pour les bytes, "PyBytes_Size()" renvoie sa longueur et
"PyBytes_AsStringAndSize()" fournit un pointeur vers sa valeur et sa
longueur. Notez que les objets bytes en Python peuvent contenir des
valeurs nulles, c'est pourquoi il ne faut pas utiliser la fonction C
"strlen()".

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 ?
=====================================================================================================

La plupart des versions pré-compilées de Python n'incluent pas le
répertoire "/usr/lib/python2.*x*/config/", qui contient les différents
fichiers nécessaires à la compilation des extensions Python.

Pour Red Hat, installez le RPM *python-devel* pour obtenir les
fichiers nécessaires.

Pour Debian, exécutez "apt-get install python-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.

Cependant, vous devez parfois exécuter l'interpréteur Python intégré
dans le même fil d’exécution que le reste de votre application et vous
ne pouvez pas laisser "PyRun_InteractiveLoop()" attendre les entrées
utilisateur. La seule solution est alors d'appeler
"PyParser_ParseString()" et de tester si "e.error" égale "E_EOF", ce
qui signifie que l'entrée est incomplète. Voici un exemple de code,
non testé, inspiré d'un code écrit par Alex Farber :

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>
   #include <node.h>
   #include <errcode.h>
   #include <grammar.h>
   #include <parsetok.h>
   #include <compile.h>

   int testcomplete(char *code)
     /* code should end in \n */
     /* return -1 for error, 0 for incomplete, 1 for complete */
   {
     node *n;
     perrdetail e;

     n = PyParser_ParseString(code, &_PyParser_Grammar,
                              Py_file_input, &e);
     if (n == NULL) {
       if (e.error == E_EOF)
         return 0;
       return -1;
     }

     PyNode_Free(n);
     return 1;
   }

Une autre solution est d'essayer de compiler la chaîne reçue avec
"Py_CompileString()". Si cela se compile sans erreur, essayez
d'exécuter l'objet code renvoyé en appelant "PyEval_EvalCode()".
Sinon, enregistrez l'entrée pour plus tard. Si la compilation échoue,
vérifiez s'il s'agit d'une erreur ou s'il faut juste plus de données —
en extrayant la chaîne de message du *n*-uplet d'exception et en la
comparant à la chaîne  *"unexpected EOF while parsing"*. Voici un
exemple complet d'utilisation de la bibliothèque *readline* de GNU (il
vous est possible d'ignorer **SIGINT** lors de l'appel à "readline()")
:

   #include <stdio.h>
   #include <readline.h>

   #define PY_SSIZE_T_CLEAN
   #include <Python.h>
   #include <object.h>
   #include <compile.h>
   #include <eval.h>

   int main (int argc, char* argv[])
   {
     int i, j, done = 0;                          /* lengths of line, code */
     char ps1[] = ">>> ";
     char ps2[] = "... ";
     char *prompt = ps1;
     char *msg, *line, *code = NULL;
     PyObject *src, *glb, *loc;
     PyObject *exc, *val, *trb, *obj, *dum;

     Py_Initialize ();
     loc = PyDict_New ();
     glb = PyDict_New ();
     PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

     while (!done)
     {
       line = readline (prompt);

       if (NULL == line)                          /* Ctrl-D pressed */
       {
         done = 1;
       }
       else
       {
         i = strlen (line);

         if (i > 0)
           add_history (line);                    /* save non-empty lines */

         if (NULL == code)                        /* nothing in code yet */
           j = 0;
         else
           j = strlen (code);

         code = realloc (code, i + j + 2);
         if (NULL == code)                        /* out of memory */
           exit (1);

         if (0 == j)                              /* code was empty, so */
           code[0] = '\0';                        /* keep strncat happy */

         strncat (code, line, i);                 /* append line to code */
         code[i + j] = '\n';                      /* append '\n' to code */
         code[i + j + 1] = '\0';

         src = Py_CompileString (code, "<stdin>", Py_single_input);

         if (NULL != src)                         /* compiled just fine - */
         {
           if (ps1  == prompt ||                  /* ">>> " or */
               '\n' == code[i + j - 1])           /* "... " and double '\n' */
           {                                               /* so execute it */
             dum = PyEval_EvalCode (src, glb, loc);
             Py_XDECREF (dum);
             Py_XDECREF (src);
             free (code);
             code = NULL;
             if (PyErr_Occurred ())
               PyErr_Print ();
             prompt = ps1;
           }
         }                                        /* syntax error or E_EOF? */
         else if (PyErr_ExceptionMatches (PyExc_SyntaxError))
         {
           PyErr_Fetch (&exc, &val, &trb);        /* clears exception! */

           if (PyArg_ParseTuple (val, "sO", &msg, &obj) &&
               !strcmp (msg, "unexpected EOF while parsing")) /* E_EOF */
           {
             Py_XDECREF (exc);
             Py_XDECREF (val);
             Py_XDECREF (trb);
             prompt = ps2;
           }
           else                                   /* some other syntax error */
           {
             PyErr_Restore (exc, val, trb);
             PyErr_Print ();
             free (code);
             code = NULL;
             prompt = ps1;
           }
         }
         else                                     /* some non-syntax error */
         {
           PyErr_Print ();
           free (code);
           code = NULL;
           prompt = ps1;
         }

         free (line);
       }
     }

     Py_XDECREF(glb);
     Py_XDECREF(loc);
     Py_Finalize();
     exit(0);
   }


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.

La bibliothèque *Boost Python Library* (BPL,
http://www.boost.org/libs/python/doc/index.html) fournit un moyen de
le faire depuis C++ (c.-à-d. que vous pouvez hériter d'une classe
d'extension écrite en C++ en utilisant BPL).
