34

我正在尝试从大型 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 。所以。

我现在正在尝试分发此扩展模块,并且很难找到描述 setup.py 分发 Cython 模块以及 C 源代码和共享库的最佳实践的资源。我已通读“安装 Python 模块”、“分发 Python 模块”和“分发 Cython 模块”。我了解如何自行分发扩展模块。我不太确定分发扩展模块的依赖项的最佳方式。

Cython 文档表明您应该包含生成的 .cpp 文件以及 .pyx 文件,以防 Cython 不存在,但它没有提供代码来演示如何最好地处理每种情况。它也没有提到如何分发 Cython 模块所依赖的共享库。

我正在挖掘来自 pandas、lxml、pyzmq、h5py 等的 setup.py 脚本,并且发生了相当多的无关工作。如果有人有可能加速此过程的指针或示例代码,我当然会很感激!

4

3 回答 3

17

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)
于 2012-11-28T10:05:59.293 回答
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。

相当棘手,但实际上它正在工作。

于 2014-11-02T10:41:48.443 回答
5

我已经推送了setuptools 288的修复程序,计划作为 setuptools 18.0 发布。此更改日志条目描述了应与该构建一起使用的技术。Beta 版本可用于测试。

于 2015-06-10T16:49:16.860 回答