Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Cython To Link Python To A Shared Library

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 written for a test. I am having trouble finding an example for setting this up.

I have a pyd/pyx file I created manually. The third party has given me a header file (*.h) and a shared library (*.so). As far as I can tell, there are no other dependencies. Can someone provide an example of how to set this up using Cython and disutils?

Thanks

like image 707
josephmisiti Avatar asked Jun 07 '13 22:06

josephmisiti


People also ask

Does Cython work with Python libraries?

Cython Hello World As Cython can accept almost any valid python source file, one of the hardest things in getting started is just figuring out how to compile your extension.

Can Cython compile all Python code?

You can't, Cython is not made to compile Python nor turn it into an executable.


1 Answers

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...

like image 177
Gauthier Boaglio Avatar answered Sep 22 '22 19:09

Gauthier Boaglio