使用Cython扩展模块分发共享库和一些C代码

Jef*_*her 33 python distutils cython

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

我现在正在尝试分发此扩展模块,并且我很难跟踪描述用于分发Cython模块以及C源代码和共享库的setup.py最佳实践的资源.我已经阅读了" 安装Python模块 "," 分发Python模块 "和" 分发Cython模块 ".我了解如何自行分发扩展模块.我不太确定分发扩展模块的依赖关系的最佳方式.

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

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

Rob*_*bon 16

1)分发libbig.so

这是python无法帮助你的问题.你是谁的目标?如果它是linux,你能否请求他们用他们的包管理器安装它?如果libbig不是通过包管理器分发的,或者它不是linux,并且您的目标是多个体系结构,则可能必须分发libbig源.

2)Cython/setuptools.

坦率地说,我认为最容易要求人们拥有Cython.这种方式有代码的唯一一个地面实况版本,你不必担心不一致.pyx.cpp代码.最简单的方法是使用setuptools而不是distutils.那样,你可以使用:

setup('mypackage',
    ...
    install_requires=['cython'])
Run Code Online (Sandbox Code Playgroud)

总的来说,您的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})
Run Code Online (Sandbox Code Playgroud)

如果你不喜欢要求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)
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在较新的`setuptools`和`distutils`版本(我使用`setuptools` 5.7)中,命令被移动到它们自己的模块中.所以你想分别从setuptools.command.build_ext import build_ext`或`distutils`做. (2认同)

Pro*_*oGM 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)
Run Code Online (Sandbox Code Playgroud)

lazy_cythonize是一个虚假列表,仅当有人试图访问它时才生成其内部元素.
在需要时,此类导入Cython.Build并生成扩展列表.这样可以避免将*.c文件保留在项目中,从而需要在构建模块时安装cython.

相当棘手,但实际上它正在发挥作用.

  • 酷模式,这也适用于在setup_requires设置运行时期间需要cython的setuptools :) (2认同)

Jas*_*mbs 5

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