Portage des modules d'extension vers Python 3
*********************************************

auteur:
   Benjamin Peterson


Résumé
^^^^^^

Changer l'API C n'était pas l'un des objectifs de Python 3, cependant
les nombreux changements au niveau Python ont rendu impossible de
garder l'API de Python 2 comme elle était. Certains changements tels
que l'unification de "int()" et "long()" sont plus apparents au niveau
C. Ce document s'efforce de documenter les incompatibilités et la
façon dont elles peuvent être contournées.


Compilation conditionnelle
==========================

La façon la plus simple de compiler seulement une section de code pour
Python 3 est de vérifier si "PY_MAJOR_VERSION" est supérieur ou égal à
3. :

   #if PY_MAJOR_VERSION >= 3
   #define IS_PY3K
   #endif

Les fonctions manquantes dans l'API peuvent être remplacées par des
alias à leurs équivalents dans des blocs conditionnels.


Modifications apportées aux API des objets
==========================================

Python 3 a fusionné certains types avec des fonctions identiques tout
en séparant de façon propre, d'autres.


Unification de *str* et *unicode*
---------------------------------

Le type "str()" de Python 3 est l'équivalent de "unicode()" sous
Python 2 ; Les fonctions C sont appelées "PyUnicode_*" pour les deux
versions. L'ancien type de chaîne de caractères de 8 bits est devenue
"bytes()", avec des fonctions C nommées "PyBytes_*". Python 2.6 et
toutes les versions supérieures fournissent un en-tête de
compatibilité, "bytesobject.h", faisant correspondre les noms
"PyBytes" aux "PyString". Pour une meilleure compatibilité avec Python
3, "PyUnicode" doit être utilisé seulement pour des données textuelles
et "PyBytes" pour des données binaires. Il est important de noter que
"PyBytes" et "PyUnicode" en Python 3 ne sont pas remplaçables
contrairement à "PyString" et "PyUnicode" dans Python 2. L'exemple
suivant montre l'utilisation optimale de "PyUnicode", "PyString", et
"PyBytes".

   #include "stdlib.h"
   #include "Python.h"
   #include "bytesobject.h"

   /* text example */
   static PyObject *
   say_hello(PyObject *self, PyObject *args) {
       PyObject *name, *result;

       if (!PyArg_ParseTuple(args, "U:say_hello", &name))
           return NULL;

       result = PyUnicode_FromFormat("Hello, %S!", name);
       return result;
   }

   /* just a forward */
   static char * do_encode(PyObject *);

   /* bytes example */
   static PyObject *
   encode_object(PyObject *self, PyObject *args) {
       char *encoded;
       PyObject *result, *myobj;

       if (!PyArg_ParseTuple(args, "O:encode_object", &myobj))
           return NULL;

       encoded = do_encode(myobj);
       if (encoded == NULL)
           return NULL;
       result = PyBytes_FromString(encoded);
       free(encoded);
       return result;
   }


Unification de *long* et *int*
------------------------------

Python 3 n'a qu'un type d'entier, "int()". Mais il correspond au type
"long()" de Python 2 — le type "int()" utilisé dans Python 2 a été
supprimé. Dans l'API C, les fonctions "PyInt_*" sont remplacées par
leurs équivalents "PyLong_*".


Initialisation et état du module
================================

Python 3 a remanié son système d'initialisation des modules
d'extension (Voir **PEP 3121**.). Au lieu de stocker les états de
module dans les variables globales, les états doivent être stockés
dans une structure spécifique à l'interpréteur. Créer des modules qui
ont un fonctionnement correct en Python 2 et Python 3 est délicat.
L'exemple suivant montre comment.

   #include "Python.h"

   struct module_state {
       PyObject *error;
   };

   #if PY_MAJOR_VERSION >= 3
   #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
   #else
   #define GETSTATE(m) (&_state)
   static struct module_state _state;
   #endif

   static PyObject *
   error_out(PyObject *m) {
       struct module_state *st = GETSTATE(m);
       PyErr_SetString(st->error, "something bad happened");
       return NULL;
   }

   static PyMethodDef myextension_methods[] = {
       {"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
       {NULL, NULL}
   };

   #if PY_MAJOR_VERSION >= 3

   static int myextension_traverse(PyObject *m, visitproc visit, void *arg) {
       Py_VISIT(GETSTATE(m)->error);
       return 0;
   }

   static int myextension_clear(PyObject *m) {
       Py_CLEAR(GETSTATE(m)->error);
       return 0;
   }


   static struct PyModuleDef moduledef = {
           PyModuleDef_HEAD_INIT,
           "myextension",
           NULL,
           sizeof(struct module_state),
           myextension_methods,
           NULL,
           myextension_traverse,
           myextension_clear,
           NULL
   };

   #define INITERROR return NULL

   PyMODINIT_FUNC
   PyInit_myextension(void)

   #else
   #define INITERROR return

   void
   initmyextension(void)
   #endif
   {
   #if PY_MAJOR_VERSION >= 3
       PyObject *module = PyModule_Create(&moduledef);
   #else
       PyObject *module = Py_InitModule("myextension", myextension_methods);
   #endif

       if (module == NULL)
           INITERROR;
       struct module_state *st = GETSTATE(module);

       st->error = PyErr_NewException("myextension.Error", NULL, NULL);
       if (st->error == NULL) {
           Py_DECREF(module);
           INITERROR;
       }

   #if PY_MAJOR_VERSION >= 3
       return module;
   #endif
   }


CObject remplacé par Capsule
============================

L'objet "Capsule" a été introduit dans Python 3.1 et 2.7 pour
remplacer "CObject". Le type "CObject" était utile, mais son API
posait des soucis : elle ne permettait pas la distinction entre les
objets C valides, ce qui permettait aux objets C assortis
incorrectement de planter l'interpréteur, et certaines des API
s'appuyaient sur un comportement indéfini en C. (Pour plus de détails
sur la logique de Capsules, veuillez consulter bpo-5630).

Si vous utilisez actuellement CObjects et que vous voulez migrer vers
la version 3.1 ou plus récente, vous devrez passer à Capsules.
"CObject" est déprécié dans 3.1 et 2.7 et est supprimé dans Python
3.2. Si vous ne gérez que les versions 2.7, ou 3.1 et supérieures,
vous pouvez simplement passer à "Capsule". Si vous avez besoin de
gérer Python 3.0, ou des versions de Python antérieures à 2.7, vous
devez gérer CObjects et Capsules. (Notez que Python 3.0 n'est plus
maintenu, et qu'il n'est pas recommandé pour une utilisation en
production).

L'exemple suivant d'en-tête de fichier "capsulethunk.h" peut résoudre
le problème. Il suffit d'écrire votre code dans l'API "Capsule" et
d'inclure ce fichier d'en-tête après "Python.h". Votre code utilisera
automatiquement Capsules dans les versions de Python avec Capsules, et
passera à CObjects lorsque les Capsules ne sont pas disponibles.

"capsulethunk.h" reproduit le fonctionnement de Capsules en utilisant
CObjects. Cependant, "CObject" ne permet pas de stocker le "nom" de la
capsule. Les objets simulés "Capsule" créés par "capsulethunk.h" se
comportent légèrement différemment des véritables Capsules. Ainsi :

   * Le paramètre *name* passé à "PyCapsule_New()" est ignoré.

   * Le paramètre *name* passé à "PyCapsule_IsValid()" et
     "PyCapsule_GetPointer()" est ignoré et il n'y a pas de
     vérification d'erreur du nom.

   * "PyCapsule_GetName()" renvoie toujours un NULL.

   * "PyCapsule_SetName()" lève toujours une exception et renvoie un
     échec. Note : Puisqu'il n'y a aucun moyen de stocker un nom dans
     un CObject, l'échec verbeux de "PyCapsule_SetName()" a été jugé
     préférable à un échec non-verbeux dans ce cas. Si cela ne vous
     convenait pas, vous pouvez modifier votre copie locale selon vos
     besoins.

Vous pouvez trouver "capsulethunk.h" dans la distribution source de
Python comme Doc/includes/capsulethunk.h. Nous l'incluons ici pour
votre confort :

   #ifndef __CAPSULETHUNK_H
   #define __CAPSULETHUNK_H

   #if (    (PY_VERSION_HEX <  0x02070000) \
        || ((PY_VERSION_HEX >= 0x03000000) \
         && (PY_VERSION_HEX <  0x03010000)) )

   #define __PyCapsule_GetField(capsule, field, default_value) \
       ( PyCapsule_CheckExact(capsule) \
           ? (((PyCObject *)capsule)->field) \
           : (default_value) \
       ) \

   #define __PyCapsule_SetField(capsule, field, value) \
       ( PyCapsule_CheckExact(capsule) \
           ? (((PyCObject *)capsule)->field = value), 1 \
           : 0 \
       ) \


   #define PyCapsule_Type PyCObject_Type

   #define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
   #define PyCapsule_IsValid(capsule, name) (PyCObject_Check(capsule))


   #define PyCapsule_New(pointer, name, destructor) \
       (PyCObject_FromVoidPtr(pointer, destructor))


   #define PyCapsule_GetPointer(capsule, name) \
       (PyCObject_AsVoidPtr(capsule))

   /* Don't call PyCObject_SetPointer here, it fails if there's a destructor */
   #define PyCapsule_SetPointer(capsule, pointer) \
       __PyCapsule_SetField(capsule, cobject, pointer)


   #define PyCapsule_GetDestructor(capsule) \
       __PyCapsule_GetField(capsule, destructor)

   #define PyCapsule_SetDestructor(capsule, dtor) \
       __PyCapsule_SetField(capsule, destructor, dtor)


   /*
    * Sorry, there's simply no place
    * to store a Capsule "name" in a CObject.
    */
   #define PyCapsule_GetName(capsule) NULL

   static int
   PyCapsule_SetName(PyObject *capsule, const char *unused)
   {
       unused = unused;
       PyErr_SetString(PyExc_NotImplementedError,
           "can't use PyCapsule_SetName with CObjects");
       return 1;
   }



   #define PyCapsule_GetContext(capsule) \
       __PyCapsule_GetField(capsule, descr)

   #define PyCapsule_SetContext(capsule, context) \
       __PyCapsule_SetField(capsule, descr, context)


   static void *
   PyCapsule_Import(const char *name, int no_block)
   {
       PyObject *object = NULL;
       void *return_value = NULL;
       char *trace;
       size_t name_length = (strlen(name) + 1) * sizeof(char);
       char *name_dup = (char *)PyMem_MALLOC(name_length);

       if (!name_dup) {
           return NULL;
       }

       memcpy(name_dup, name, name_length);

       trace = name_dup;
       while (trace) {
           char *dot = strchr(trace, '.');
           if (dot) {
               *dot++ = '\0';
           }

           if (object == NULL) {
               if (no_block) {
                   object = PyImport_ImportModuleNoBlock(trace);
               } else {
                   object = PyImport_ImportModule(trace);
                   if (!object) {
                       PyErr_Format(PyExc_ImportError,
                           "PyCapsule_Import could not "
                           "import module \"%s\"", trace);
                   }
               }
           } else {
               PyObject *object2 = PyObject_GetAttrString(object, trace);
               Py_DECREF(object);
               object = object2;
           }
           if (!object) {
               goto EXIT;
           }

           trace = dot;
       }

       if (PyCObject_Check(object)) {
           PyCObject *cobject = (PyCObject *)object;
           return_value = cobject->cobject;
       } else {
           PyErr_Format(PyExc_AttributeError,
               "PyCapsule_Import \"%s\" is not valid",
               name);
       }

   EXIT:
       Py_XDECREF(object);
       if (name_dup) {
           PyMem_FREE(name_dup);
       }
       return return_value;
   }

   #endif /* #if PY_VERSION_HEX < 0x02070000 */

   #endif /* __CAPSULETHUNK_H */


Autres options
==============

Si vous écrivez un nouveau module d'extension, vous pouvez envisager
d'utiliser Cython. Il traduit un langage de type Python en C. Les
modules d'extension qu'il crée sont compatibles avec Python 3 et
Python 2.
