使用cython创建包,这样用户就可以安装它而无需安装cython

Lef*_*fty 7 python distutils cython

我有个问题.我想分发我的cython驱动的软件包,但我认为在setup.py中构建它们没有简单的方法.我想setup.py:

  • 最重要的是:安装我的包没有cython(从预先生成的C文件或事先安装cython)
  • 在sdist上重建(运行cythonize)包
  • 不需要硬编码我的cython模块列表(只需使用glob或其他东西)
  • 能够在没有.c文件(不应该存储在git中)或.pyx(可能不会分发)的情况下工作.当然,这些集合中至少有一个将始终存在.

目前在我的发痒包中,我正在使用这个非常复杂的代码:

import os
from glob import glob
from distutils.command.build_ext import build_ext as _build_ext
from distutils.command.sdist import sdist as _sdist
from distutils.core import setup
from distutils.core import Extension



def generate_extensions():
    return [
        # Compile cython-generated .c files into importable .so libraries.
        Extension(os.path.splitext(name)[0], [name])
        for name in C_FILES
    ]


# In distribution version, there are no pyx files, when you clone package from git, there will be no c files.
CYTHON_FILES = glob('itchy/*.pyx')
C_FILES = glob('itchy/*.c')
extensions = generate_extensions()


class build_ext(_build_ext):
    def run(self):
        # Compile cython files (.pyx, some of the .py) into .c files if Cython is available.
        try:
            from Cython.Build import cythonize
            if CYTHON_FILES:
                cythonize(CYTHON_FILES)

                # Update C_FILES in case they were originally missing.
                global C_FILES, extensions
                C_FILES = glob('itchy/*.c')
                extensions = generate_extensions()
            else:
                print('No .pyx files found, building extensions skipped. Pre-built versions will be used.')
        except ImportError:
            print('Cython is not installed, building extensions skipped. Pre-built versions will be used.')
            assert C_FILES, 'C files have to be present in distribution or Cython has to be installed'
        _build_ext.run(self)


class sdist(_sdist):
    def run(self):
        # Make sure the compiled Cython files in the distribution are up-to-date
        self.run_command("build_ext")
        _sdist.run(self)


setup(
    (...)
    ext_modules = extensions,
    cmdclass = {
        'build_ext': build_ext,
        'sdist': sdist,
    },
)
Run Code Online (Sandbox Code Playgroud)

dan*_*nny 5

通常通过尝试导入cython并调整扩展名来完成

  1. 如果存在cython,则使用cython构建pyx文件
  2. 如果没有cython,则构建C文件

例如:

try:
    from Cython.Distutils.extension import Extension
    from Cython.Distutils import build_ext
except ImportError:
    from setuptools import Extension
    USING_CYTHON = False
else:
    USING_CYTHON = True

ext = 'pyx' if USING_CYTHON else 'c'
sources = glob('my_module/*.%s' % (ext,))
extensions = [
    Extension(source.split('.')[0].replace(os.path.sep, '.'),
              sources=[source],
    )
for source in sources]
cmdclass = {'build_ext': build_ext} if USING_CYTHON else {}

setup(<..>, ext_modules=extensions, cmdclass=cmdclass)
Run Code Online (Sandbox Code Playgroud)

这些source.split东西是必需的,因为cython化的扩展名需要在表单中,my_module.ext而glob需要路径名my_module/ext.

有关实际示例,请参阅此存储库.

但是,您应该.c在git仓库中包含文件以及可分发文件,否则在构建分发版时,.c文件将被重新构建,并且可能与您的计算机上构建的文件相同或不同.

例如,它们可以由另一个版本的cython构建,或者在不同的平台上构建,产生不同的代码.

Cython是一个静态编译器 - 建议将它生成的文件提交到存储库.

强烈建议您分发生成的.c文件以及Cython源代码,以便用户可以安装模块而无需使用Cython.

请参阅有关分发模块的Cython文档.

  • 在这种情况下,生成的 C 文件实际上并不是源代码,因为它们不是目标的源代码(.pyx 文件是源代码)。如果您希望用户拥有相同的编译目标,那么您应该分发目标(例如作为轮子) (3认同)
  • 我不同意将cython生成的C文件存储在您的仓库中是个好主意,尤其是在cython代码经常更改的情况下。相反,您可以这样做,以便在发布版本时将.c文件包括在您的源代码发行版中,但如果开发人员正在从回购中构建项目,则仍然要求开发人员从cython生成C文件。这样,您可以避免自动生成的文件中不断出现的代码混乱。 (2认同)