使用Cython扩展模块分发共享库和一些C代码 我试图从一个大型C++共享库(LIbBug,so)中获取一些函数,并通过Cython将它们公开给Python。为此,我得到了一个小C++文件(小.CPP),它提供了一个围绕我需要的共享库的功能的薄包装,它使Cython(PySmall .Pyx)很容易调用。

使用Cython扩展模块分发共享库和一些C代码 我试图从一个大型C++共享库(LIbBug,so)中获取一些函数,并通过Cython将它们公开给Python。为此,我得到了一个小C++文件(小.CPP),它提供了一个围绕我需要的共享库的功能的薄包装,它使Cython(PySmall .Pyx)很容易调用。,python,cython,distutils,Python,Cython,Distutils,libbig.so->small.cpp,small.h->libsmall.so->pysmall.pyx->pysmall.cpp->pysmall.so 我可以在自己的计算机上构建并运行这个扩展模块:我只需将small.cpp编译成libsmall.so,然后在setup.py中的扩展对象中说“libraries=['small']”来构建扩展模块pysmall.so 我现在正试图分发这个扩展模块,我很难找到描述setup.py分发Cython模块以及C源代码和共享库的最佳实践的资源。我已

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

我可以在自己的计算机上构建并运行这个扩展模块:我只需将small.cpp编译成libsmall.so,然后在setup.py中的扩展对象中说“libraries=['small']”来构建扩展模块pysmall.so

我现在正试图分发这个扩展模块,我很难找到描述setup.py分发Cython模块以及C源代码和共享库的最佳实践的资源。我已通读了“”和“”。我了解如何独立分发扩展模块。我不太确定分发扩展模块依赖项的最佳方法

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

我正在翻阅pandas、lxml、pyzmq、h5py等的setup.py脚本,还有很多无关的工作正在进行。如果任何人有可能加速这个过程的指针或示例代码,我当然会非常感激

1)分布libbig.so

这是一个python无法帮助您解决的问题。你的目标是谁?如果是linux,你能要求他们用软件包管理器安装它吗?如果libbig不是通过包管理器分发的,或者它不是linux,并且您的目标是多个体系结构,那么您可能必须分发libbig源代码

2) Cython/setuptools

坦白地说,我认为最简单的方法就是要求人们有赛昂。这样,代码只有一个基本事实版本,您不必担心
.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})
# 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)
如果您不喜欢使用cython的想法,您可以采取以下措施:

# 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})
# 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)

这是我棘手的解决办法。我们的想法是“隐藏”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


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

我已经推出了一个补丁,计划作为setuptools 18.0发布。描述应与该生成一起使用的技术。A可用于测试。

请注意,在较新的
setuptools
distutils
版本(我使用的是
setuptools
5.7)中,命令被移动到它们自己的模块中。因此,您可能希望分别从setuptools.command.build\u ext导入build\u ext或从
distutils
执行
。您的第一个setup.py是它在有机会安装Cython.distutils之前导入Cython.distutils(如果它不存在)。另一个选项是创建一个conda包,它可以捆绑在
libbig.So
中。很酷的模式,这也适用于在安装运行时需要cython的setuptools和setup_requires:)这正是我想要的。当Cython在脚本顶部导入时,感觉需要Cython才能安装。如果用户有较旧版本的setuptools怎么办?@RichardHansen您有两个选择。您可以要求用户升级(现在可能还太早),捆绑ez_setup.py并在安装软件包期间强制升级setuptools(不推荐),或者检测setuptools版本并退回到不太优雅的方法。仍然必须使用setuptools>=18中的惰性列表模式解决方法,因为安装需要deps(!=cython)@JasonR。Coombs@marscher:听起来不错。Cython之所以是特例,是因为它支持烘焙到setuptools中的Cython,而且Cython是一个构建工具。我可以看到一个通用钩子的优点,它允许在调用setup()之前,但在解决setup_需要的问题之后,注入行为。