web-dev-qa-db-fra.com

Utilisation de Cython pour lier Python à une bibliothèque partagée

J'essaie d'intégrer une bibliothèque tierce écrite en C avec mon application python en utilisant Cython. J'ai tout le code python écrit pour un test. J'ai du mal à trouver un exemple pour configurer cela.

J'ai un fichier pyd/pyx Que j'ai créé manuellement. Le tiers m'a donné une header file (*.h) et une shared library (*.so). Pour autant que je sache, il n'y a pas d'autres dépendances. Quelqu'un peut-il fournir un exemple de la façon de configurer cela à l'aide de Cython et disutils?

Merci

37
josephmisiti

Sûr !

(Dans ce qui suit, je suppose que vous savez déjà comment gérer cimport et les interactions entre .pxd Et .pyx. Si ce n'est pas complètement le cas, il suffit de demander et Je développerai également cette partie)

L'exemple (extrait d'un de mes projets C++, mais un projet C fonctionnerait à peu près de la même manière):

1. Le fichier d'installation de Distutils:

En supposant que l'extension à créer sera appelée myext et que la bibliothèque partagée tierce est libexternlib.so (Notez la lib * préfixe, ici) ...

# setup.py file
import sys
import os
import shutil

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

# clean previous build
for root, dirs, files in os.walk(".", topdown=False):
    for name in files:
        if (name.startswith("myext") and not(name.endswith(".pyx") or name.endswith(".pxd"))):
            os.remove(os.path.join(root, name))
    for name in dirs:
        if (name == "build"):
            shutil.rmtree(name)

# build "myext.so" python extension to be added to "PYTHONPATH" afterwards...
setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [
        Extension("myext", 
                  sources=["myext.pyx",
                           "SomeAdditionalCppClass1.cpp",
                           "SomeAdditionalCppClass2.cpp"
                       ],
                  libraries=["externlib"],          # refers to "libexternlib.so"
                  language="c++",                   # remove this if C and not C++
                  extra_compile_args=["-fopenmp", "-O3"],
                  extra_link_args=["-DSOME_DEFINE_OPT", 
                                   "-L./some/extra/dependency/dir/"]
             )
        ]
)           

Remarque: Votre fichier externe .so Est lié via l'option libraries:

libraries=["externlib"]   # Without the 'lib' prefix and the '.so' extension...

Remarque: l'option sources peut être utilisée pour obtenir des fichiers source supplémentaires compilés.

Important: myext.pxd (Ne pas confondre avec .pyd - Windows) et myext.pyx Devrait être dans le même répertoire. Au moment de la compilation, le fichier de définition, s'il existe, est traité en premier ( plus ).

2. Ensuite, exécutez-le comme suit:

Après avoir changé le répertoire en celui contenant votre myext.pxd, Votre myext.pyx, Ainsi que le script setup.py Ci-dessus:

# setup.sh
# Make the "myext" Python Module ("myext.so")
CC="gcc"   \
CXX="g++"   \
CFLAGS="-I./some/path/to/includes/ -I../../../DEPENDENCIES/python2.7/inc -I../../../DEPENDENCIES/gsl-1.15"   \
LDFLAGS="-L./some/path/to/externlib/"   \
    python setup.py build_ext --inplace

Où :

  • libexternlib.so Est supposé être situé à ./some/path/to/externlib/
  • yourheader.h Est supposé être situé à ./some/path/to/includes/

Remarque: CFLAGS aurait également pu être configuré à l'aide de l'option extra_compile_args:

extra_compile_args=["-I./some/path/to/includes/", "-fopenmp", "-O3"]

Remarque: LDFLAGS aurait également pu être configuré à l'aide de l'option extra_link_args:

extra_link_args=["-L./some/path/to/externlib/", "-DSOME_DEFINE_OPT", "-L./some/extra/dependency/dir/"]

Une fois distutils fait avec la construction, vous obtenez de nouveaux fichiers, spécialement le myext.cpp, myext.h Et surtout, le myext.so.

3. Après cela, vous êtes prêt à partir:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./some/path/to/externlib/
export PYTHONPATH=$PYTHONPATH:./some/path/to/myext/

# Run some script requiring "myext.so"
python somescript.py

Où votre nouvelle extension Python extension peut être importée par son nom:

# somescript.py
import myext
from myext import PySomeFeature
...

Remarque sur l'optimisation: par défaut, -O2 Est utilisé pour compiler l'extension, mais cela peut être surchargé (voir la configuration ci-dessus où -O3 Est spécifié).

Remarque sur les chemins Cython: Si Cython a été installé dans un répertoire personnalisé, vous voudrez peut-être l'ajouter à votre environnement, avant tout:

PYTHONPATH=$PYTHONPATH:../../../DEPENDENCIES/Cython-0.18 export PYTHONPATH;
PATH=$PATH:../../../DEPENDENCIES/Cython-0.18/bin; export PATH;

Eh bien, j'espère avoir couvert les points principaux ...

57
Gauthier Boaglio