# 4. Construire des extensions C et C++

Une extension C pour CPython est une bibliothèque partagée (Un `.so` sur Linux, un `.pyd` sur Windows), qui expose une fonction d'initialisation.

Pour pouvoir être importée, la bibliothèque partagée doit pourvoir être trouvée dans `PYTHONPATH`, et doit porter le nom du module, avec l'extension appropriée. En utilisant distutils, le nom est généré automatiquement.

La fonction d'initialisation doit avoir le prototype :

PyObject *`PyInit_modulename`(void)

It returns either a fully initialized module, or a `PyModuleDef` instance. See Initializing C modules for details.

Pour les modules dont les noms sont entièrement en ASCII, la fonction doit être nommée `PyInit_<modulename>`, dont `<modulename>` est remplacé par le nom du module. En utilisant Multi-phase initialization, il est possible d'utiliser des noms de modules comptant des caractères non ASCII. Dans ce cas, le nom de la fonction d'initialisation est `PyInitU_<modulename>`, où `modulename` est encodé avec l'encodage punyencode de Python, dont les tirets sont remplacés par des tirets-bas. En Python ça donne :

    def initfunc_name(name):
        try:
            suffix = b'_' + name.encode('ascii')
        except UnicodeEncodeError:
            suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
        return b'PyInit' + suffix

Il est possible d'exporter plusieurs modules depuis une seule bibliothèque partagée en définissant plusieurs fonctions d'initialisation. Cependant pour les importer, un lien symbolique doit être créé pour chacun, ou un importer personnalisé, puisque par défaut seule la fonction correspondant au nom du fichier est cherchée. Voir le chapitre "Multiple modules in one library" dans la PEP 489 pour plus d'informations.

## 4.1. Construire les extensions C et C++ avec distutils

Des modules d'extension peuvent être construits avec distutils, qui est inclus dans Python. Puisque distutils gère aussi la création de paquets binaires, les utilisateurs n'auront pas nécessairement besoin ni d'un compilateur ni de distutils pour installer l'extension.

Un paquet distutils contient un script `setup.py`. C'est un simple fichier Python, ressemblant dans la plupart des cas à :

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           ext_modules = [module1])

Avec ce `setup.py` et un fichier `demo.c`, lancer

    python setup.py build

compilera `demo.c`, et produira un module d'extension nommé `demo` dans le dossier `build`. En fonction du système, le fichier du module peut se retrouver dans `build/lib.system`, et son nom peut être `demo.py` ou `demo.pyd`.

Dans le fichier `setup.py`, tout est exécuté en appelant la fonction `setup`. Elle prend un nombre variable d'arguments nommés, dont l'exemple précédent n'utilise qu'une partie. L'exemple précise des méta-informations pour construire les paquets, et définir le contenu du paquet. Normalement un paquet contient des modules additionnels, comme des modules sources, documentation, sous paquets, etc. Référez-vous à la documentation de distutils dans Distribuer des modules Python (Version historique) pour en apprendre plus sur les fonctionnalités de distutils. Cette section n'explique que la construction de modules d'extension.

Il est classique de pré-calculer les arguments à la fonction `setup()`, pour plus de lisibilité. Dans l'exemple ci-dessus, l'argument `ext_modules` à `setup()` est une liste de modules d'extension, chacun est une instance de la classe `Extension`. Dans l'exemple, l'instance définit une extension nommée `demo` construite par la compilation d'un seul fichier source `demo.c`.

Dans la plupart des cas, construire une extension est plus complexe à cause des bibliothèques et définitions de préprocesseurs dont la compilation pourrait dépendre. C'est ce qu'on remarque dans l'exemple plus bas.

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        define_macros = [('MAJOR_VERSION', '1'),
                                         ('MINOR_VERSION', '0')],
                        include_dirs = ['/usr/local/include'],
                        libraries = ['tcl83'],
                        library_dirs = ['/usr/local/lib'],
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           author = 'Martin v. Loewis',
           author_email = 'martin@v.loewis.de',
           url = 'https://docs.python.org/extending/building',
           long_description = '''
    This is really just a demo package.
    ''',
           ext_modules = [module1])

Dans cet exemple, la fonction `setup()` est appelée avec quelques autres méta-informations, ce qui est recommandé pour distribuer des paquets. En ce qui concerne l'extension, sont définis quelques macros préprocesseur, dossiers pour les en-têtes et bibliothèques. En fonction du compilateur, distutils peut donner ces informations de manière différente. Par exemple, sur Unix, ça peut ressembler aux commandes

    gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o

    gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so

Ces lignes ne sont qu'à titre d'exemple, les utilisateurs de distutils doivent avoir confiance en distutils qui fera les appels correctement.

## 4.2. Distribuer vos modules d'extension

Lorsqu'une extension a été construite avec succès, il existe trois moyens de l'utiliser.

Typiquement, les utilisateurs vont vouloir installer le module, ils le font en exécutant

    python setup.py install

Les mainteneurs de modules voudront produire des paquets source, pour ce faire ils exécuteront

    python setup.py sdist

Dans certains cas, des fichiers supplémentaires doivent être inclus dans une distribution source : c'est possible via un fichier `MANIFEST.in`, c.f. Spécifier les fichiers à distribuer.

Si la distribution source a été construite avec succès, les mainteneurs peuvent aussi créer une distribution binaire. En fonction de la plateforme, une des commandes suivantes peut être utilisée.

    python setup.py bdist_rpm
    python setup.py bdist_dumb