Using Cython To Link Python To A Shared Library

前端 未结 1 986
囚心锁ツ
囚心锁ツ 2020-11-28 20:03

I am trying to integrate a third party library written in C with my python application using Cython. I have all of the python code wri

相关标签:
1条回答
  • 2020-11-28 20:40

    Sure !

    (In the following, I assume that you already know how to deal with cimport and the interactions between .pxd and .pyx. If this is not completely the case, just ask and I will develop that part as well)

    The sample (grabbed from a C++ project of mine, but a C project would work pretty much the same) :

    1. The Distutils setup file :

    Assuming that the extension to be created will be called myext and the 3rd party shared library is libexternlib.so (note the lib* prefix, here)...

    # 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/"]
                 )
            ]
    )           
    

    Note : Your external .so file is linked via the libraries option :

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

    Note : the sources option can be used to get some additional source files compiled.

    Important : myext.pxd (do not confound with .pyd - Windows stuff) and myext.pyx should be in the same directory. At compile time the definition file, if it exists, is processed first (more).

    2. Then run it as follows :

    After having changed directory to the one containing your myext.pxd, your myext.pyx, as well as the above setup.py script :

    # 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
    

    Where :

    • libexternlib.so is assumed to be located at ./some/path/to/externlib/
    • yourheader.h is assumed to be located at ./some/path/to/includes/

    Note : CFLAGS could also have been setup using the extra_compile_args option :

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

    Note : LDFLAGS could also have been setup using the extra_link_args option :

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

    Once distutils is done with the build, you get some new files, specially the myext.cpp, myext.h and most importantly, the myext.so.

    3. After that, you're good to go :

    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
    

    Where your freshly created Python extension can be imported by its name :

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

    Note about Optimization : By default -O2 is used for compiling the extension, but this can be overloaded (see above setup where -O3 is specified).

    Note about Cython paths : If Cython was installed in a custom directory, you might want to add it to your environment, before all :

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

    Well, hope I covered the main points...

    0 讨论(0)
提交回复
热议问题