我正在尝试从大型C ++共享库(libbig.so)中获取一些函数,并通过Cython将它们暴露给Python。为此,我有一个小的C ++文件(small.cpp),它提供了我需要的共享库功能的薄包装,以便通过Cython(pysmall.pyx)调用它。
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脚本,并且发生了相当多的无关工作。如果有人有指针或示例代码可能会加速这个过程,我当然会很感激!
答案 0 :(得分:16)
1)分发libbig.so
这是python无法帮助你的问题。你是谁的目标?如果它是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)
答案 1 :(得分: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 :(得分:5)
我推出了setuptools 288的修复程序,计划发布为setuptools 18.0。 This changelog entry描述了一种适用于该构建的技术。 beta release可用于测试。