2017-10-17 138 views
3

我有问题。我想分发我的cython驱动的包,但我看不到在setup.py中构建它们的简单方法。我想setup.py到:使用cython创建包,以便用户无需安装cython即可安装它

  • 最重要的是:安装我的包没有用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, 
    }, 
) 

回答

3

通常是由试图导入用Cython和调整扩展要么

  1. 构建PYX文件与用Cython完成如果有cython
  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) 

是需要的,source.split东西作为cythonized扩展名需要在窗体my_module.ext而水珠需要像my_module/ext路径名。

See this repository为一个现实世界的例子。

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

它们可能由另一个版本的cython构建,例如,或在不同的平台上生成不同的代码。

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

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

See Cython documentation on distributing modules

+0

感谢您的回答!这很容易解决大部分问题。 但是,这需要我分发.pyx文件,以防安装我的软件包的用户安装了Cython。另外,如果我忘记在sdist之前运行build_ext,则创建的程序包将不会保持最新。 – Lefty

+0

一般建议同时发货,是的。另一种方法是需要一个env变量来启用Cython构建,否则默认构建C文件。这将否定必须分发pyx文件。最后一条评论强化了C文件应该回购的原因 - 创建分发应该永远不要依赖于生成不在存储库中的东西。 – danny

+0

如果将USING_CYTHON else {}'添加到您的示例代码中,您可以添加'cmdclass = {'build_ext':build_ext}吗?在此之后,它很好:) (我更喜欢'扩展名(file_name.split('。')[0] .replace(os.path.sep,'。'),sources = [file_name])for file_name在源代码和cythonize导入是不需要的) – Lefty