简体   繁体   English

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

[英]Distributing a shared library and some C code with a Cython extension module

I'm trying to take some functions from a large C++ shared library (libbig.so) and expose them to Python via Cython. 我正在尝试从大型C ++共享库(libbig.so)中获取一些函数,并通过Cython将它们暴露给Python。 To do so, I've got a little C++ file (small.cpp) that provides a thin wrapper around the functionality from the shared library that I need, in a way that makes it easy to call via Cython (pysmall.pyx). 为此,我有一个小的C ++文件(small.cpp),它提供了我需要的共享库功能的薄包装,以便通过Cython(pysmall.pyx)调用它。

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

I can build and run this extension module on my own computer: I just compile small.cpp into libsmall.so, and then say "libraries=['small']" in the Extension object in setup.py to build the extension module pysmall.so. 我可以在自己的计算机上构建和运行这个扩展模块:我只是将small.cpp编译成libsmall.so,然后在setup.py的Extension对象中说“libraries = ['small']”来构建扩展模块pysmall 。所以。

I'm now trying to distribute this extension module, and I am having a hard time tracking down resources that describe setup.py best practices for distributing a Cython module as well as C source and shared libraries. 我现在正在尝试分发此扩展模块,我很难跟踪描述用于分发Cython模块以及C源代码和共享库的setup.py最佳实践的资源。 I've read through " Installing Python Modules ", " Distributing Python Modules ", and " Distributing Cython Modules ". 我已经阅读了“ 安装Python模块 ”,“ 分发Python模块 ”和“ 分发Cython模块 ”。 I understand how to distribute an extension module on its own. 我了解如何自行分发扩展模块。 I'm less certain about the best way to distribute the dependencies of the extension module. 我不太确定分发扩展模块的依赖关系的最佳方式。

The Cython documentation indicates that you should include the generated .cpp files as well as the .pyx files, in case Cython is not present, yet it does not provide code to demonstrate how to best handle each situation. Cython文档表明您应该包含生成的.cpp文件以及.pyx文件,以防Cython不存在,但它不提供代码来演示如何最好地处理每种情况。 It also does not mention how to distribute the shared libraries on which the Cython module depends. 它也没有提到如何分发Cython模块所依赖的共享库。

I'm digging through the setup.py scripts from pandas, lxml, pyzmq, h5py, and more, and there's quite a bit of extraneous work happening. 我正在挖掘来自pandas,lxml,pyzmq,h5py等的setup.py脚本,并且发生了相当多的无关紧要的工作。 If anyone has pointers or example code that might accelerate this process, I'd certainly appreciate it! 如果有人有指针或示例代码可能会加速这个过程,我当然感激不尽!

1) Distributing libbig.so 1)分发libbig.so

This is a problem that python isn't going to help you with. 这是python无法帮助你的问题。 Who are you targeting? 你是谁的目标? If it's linux, can you request that they install it with their package manager? 如果它是linux,你能否请求他们用他们的包管理器安装它? If libbig isn't distributed through a package manager or it's not linux and you're targeting multiple architectures, you might have to distribute the libbig source. 如果libbig不是通过包管理器分发的,或者它不是linux,并且您的目标是多个体系结构,则可能必须分发libbig源。

2) Cython/setuptools. 2)Cython / setuptools。

Frankly, I think its easiest to just require that people have Cython. 坦率地说,我认为最容易要求人们拥有Cython。 This way there's only one ground truth version of the code, and you don't have to worry about inconsistencies between the .pyx and .cpp code. 这种方式只有一个基本真实版本的代码,您不必担心.pyx.cpp代码之间的不一致。 The easiest way to do this is to use setuptools instead of distutils . 最简单的方法是使用setuptools而不是distutils That way, you can use: 那样,你可以使用:

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

In total, your setup.py script will look something like: 总的来说,您的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})

If you don't like the idea of requiring cython, you could do something like: 如果你不喜欢要求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)

Here's my tricky solution. 这是我棘手的解决方案。 The idea is to "hide" the presence of cython until it's installed by the requirements. 这个想法是“隐藏” cython的存在,直到它被需求安装。 This can be achieved by lazy evaluation. 这可以通过惰性评估来实现。 Here's an example: 这是一个例子:

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 is a fake list that generates its internal elements only when someone tries to access to it. lazy_cythonize是一个虚假列表,仅当有人试图访问它时才生成其内部元素。
When it's required, this class imports Cython.Build and generates the list of extensions. 当需要时,该类导入Cython.Build并生成扩展列表。 This avoids to keep the *.c files in your project, requiring cython to be installed when the module is building. 这样可以避免在项目中保留*.c文件,这需要在构建模块时安装cython。

Quite tricky, but actually it's working. 相当棘手,但实际上它正在发挥作用。

I've pushed a fix for setuptools 288 , slated for release as setuptools 18.0. 我已经推出了setuptools 288的修复程序,计划发布为setuptools 18.0。 This changelog entry describes a technique that should work with that build. 此更改日志条目描述了应该与该构建一起使用的技术。 A beta release is available for testing. 测试版可用于测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM