2012-06-13 63 views
33

我試圖從一個大的C++共享庫(libbig.so)中獲取一些函數並通過Cython將它們暴露給Python。爲此,我已經有了一個C++文件(small.cpp),它以我需要的共享庫的功能提供了一個薄的包裝器,以便通過Cython(pysmall.pyx)調用。使用Cython擴展模塊分發共享庫和一些C代碼

libbig.so - > small.cpp,small.h - > libsmall.so - > pysmall.pyx - > pysmall.cpp - > pysmall.so

我可以建立並運行此擴展模塊我自己的計算機:我只是將small.cpp編譯到libsmall.so中,然後在setup.py的Extension對象中聲明「libraries = ['small']」來構建擴展模塊pysmall.so。

我現在試圖分發這個擴展模塊,並且我很難追蹤描述分發Cython模塊以及C源代碼和共享庫的setup.py最佳實踐的資源。我已閱讀「Installing Python Modules」,「Distributing Python Modules」和「Distributing Cython Modules」。我瞭解如何自行分發擴展模塊。我不太確定分發擴展模塊的依賴關係的最佳方式。

Cython文檔指出,您應該包括生成的.cpp文件以及.pyx文件,以防Cython不存在,但它不提供代碼來演示如何最好地處理每種情況。它也沒有提到如何分發Cython模塊所依賴的共享庫。

我正在從pandas,lxml,pyzmq,h5py等等的setup.py腳本中進行挖掘,並且發生了一些額外的工作。如果任何人有指針或示例代碼可能會加速這一過程,我當然會很感激!

回答

16

1)分發libbig.so

這是蟒蛇是不會幫助您解決問題。你瞄準誰?如果是linux,你能否請求他們使用他們的軟件包管理器進行安裝?如果libbig不是通過軟件包管理器分發的,或者它不是linux,並且您的目標是多個體繫結構,則可能必須分發libbig源文件。

2)Cython/setuptools。

坦率地說,我認爲最簡單的就是要求人們擁有Cython。這樣,只有一個地面實況版本的代碼,並且您不必擔心.pyx.cpp代碼之間的不一致。最簡單的方法是使用setuptools而不是distutils。這樣一來,你可以使用:

setup('mypackage', 
    ... 
    install_requires=['cython']) 

在總,你的setup.py腳本看起來像:

# setup.py 

from setuptools import setup, Extension 
from Cython.Distutils import build_ext 

pysmall = Extension('pysmall', 
    sources = ['pysmall.pyx', 'small.cpp'], 
    include_dirs = ['include/']) 

setup(name='mypackage', 
     packages=['yourpurepythonpackage'], 
     install_requires=['cython==0.17'], 
     ext_modules=[pysmall], 
     cmdclass = {'build_ext': build_ext}) 

如果你不喜歡,需要用Cython,你可以做類似的想法:

# setup.py 

import warnings 
try: 
    from Cython.Distutils import build_ext 
    from setuptools import setup, Extension 
    HAVE_CYTHON = True 
except ImportError as e: 
    HAVE_CYTHON = False 
    warnings.warn(e.message) 
    from distutils.core import setup, Extension 
    from distutils.command import build_ext 

pysmall = Extension('pysmall', 
    sources = ['pysmall.pyx', 'small.cpp'], 
    include_dirs = ['include/']) 

configuration = {'name': 'mypackage', 
     'packages': ['yourpurepythonpackage'], 
     'install_requires': ['cython==0.17'], 
     'ext_modules': [pysmall], 
     'cmdclass': {'build_ext': build_ext}} 

if not HAVE_CYTHON: 
    pysmall.sources[0] = 'pysmall.cpp' 
    configuration.pop('install_requires') 

setup(**configuration) 
+2

請注意,在更新的'setuptools'和'distutils'版本中(我使用'setuptools' 5.7),這些命令被移入它們自己的模塊中。所以你會想''setuptools.command.build_ext import build_ext'或者'distutils'分別執行。 – Midnighter

+1

您的第一個setup.py文件是在Cython.Distutils沒有安裝它的機會之前導入它。 – zneak

+0

另一種選擇是創建一個conda包,它可以捆綁在'libbig.so'中。 https://conda.io/docs/user-guide/tutorials/build-postgis.html – oLas

7

這是我的棘手的解決方案。這個想法是「隱藏」cython的存在,直到按要求安裝。這可以通過懶惰評估來實現。這裏有一個例子:

from setuptools import setup, Extension 

class lazy_cythonize(list): 
    def __init__(self, callback): 
     self._list, self.callback = None, callback 
    def c_list(self): 
     if self._list is None: self._list = self.callback() 
     return self._list 
    def __iter__(self): 
     for e in self.c_list(): yield e 
    def __getitem__(self, ii): return self.c_list()[ii] 
    def __len__(self): return len(self.c_list()) 

def extensions(): 
    from Cython.Build import cythonize 
    ext = Extension('native_ext_name', ['your/src/*.pyx']) 
    return cythonize([ext]) 


configuration = { 
    'name': 'mypackage', 
    'packages': ['yourpurepythonpackage'], 
    'install_requires': ['cython==0.17'], 
    'ext_modules': lazy_cythonize(extensions) 
} 

setup(**configuration) 

lazy_cythonize只有當有人試圖訪問它,產生它的內部元素假名單。
需要時,此類別將導入Cython.Build並生成擴展名列表。這樣可以避免將*.c文件保留在您的項目中,因此需要在構建模塊時安裝cython。

非常棘手,但實際上它的工作。

+2

酷酷的模式,這也適用於setuptools在setup setup期間需要使用cython setup_requires :) – marscher

+0

這正是我所期待的。感到蹩腳的安裝 - 需要Cython,當Cython被導入腳本的頂部時。 – csl

5

我推送了一個修復程序setuptools 288,預定爲setuptools 18.0發佈。 This changelog entry描述了一種應該與該構建一起工作的技術。 A beta release可用於測試。

+0

如果用戶擁有舊版本的setuptools,該怎麼辦? –

+0

@RichardHansen你有幾個選擇。您可以要求用戶升級(現在可能爲時過早),捆綁ez_setup.py並在安裝軟件包期間強制setuptools升級(不推薦),或者檢測setuptools版本並回退到不太優雅的方法。 –

+0

仍然必須使用setuptools> = 18中的懶惰列表模式for setup_requires deps(!= cython)@ JasonR.Coombs – marscher