如何构建包含Cython代码的Python包结构
我想制作一个包含一些Cython 代码的Python包。我已经把Cython代码搞定了。不过,现在我想知道怎么最好地打包它。
对于大多数只想安装这个包的人,我想包含Cython生成的.c
文件,并安排setup.py
来编译这个文件,生成模块。这样用户就不需要安装Cython就能安装这个包。
但是对于那些可能想修改这个包的人,我也想提供Cython的.pyx
文件,并且以某种方式让setup.py
使用Cython来构建它们(所以这些用户就需要安装Cython)。
我应该如何组织包里的文件,以便同时满足这两种情况呢?
Cython的文档给出了一些指导。但它没有说明如何制作一个单一的setup.py
来处理有Cython和没有Cython的情况。
10 个回答
强烈建议你在发布你的模块时,除了包含Cython源代码外,还要把生成的.c文件一起分发。这样用户就可以安装你的模块,而不需要先安装Cython。
另外,建议你在发布的版本中默认不要启用Cython编译。即使用户已经安装了Cython,他们可能也不想为了安装你的模块而去使用它。而且,用户安装的Cython版本可能和你使用的版本不同,这样可能会导致编译出错。
这就是说,你随模块一起提供的setup.py文件应该只是一个普通的distutils文件,专门针对生成的.c文件。对于基本示例,我们应该这样做:
from distutils.core import setup from distutils.extension import Extension setup( ext_modules = [Extension("example", ["example.c"])] )
补充一下Craig McQueen的回答:下面是如何重写sdist
命令,让Cython在创建源代码包之前自动编译你的源文件。
这样一来,你就不必担心不小心分发了过时的C
源代码。如果你对分发过程的控制有限,比如在持续集成中自动创建分发包时,这个方法也会很有帮助。
from distutils.command.sdist import sdist as _sdist
...
class sdist(_sdist):
def run(self):
# Make sure the compiled Cython files in the distribution are up-to-date
from Cython.Build import cythonize
cythonize(['cython/mycythonmodule.pyx'])
_sdist.run(self)
cmdclass['sdist'] = sdist
我现在自己做了这个,创建了一个Python包叫做 simplerandom
(BitBucket 仓库 - 更新:现在在 GitHub)。我并不指望这个包会很受欢迎,但这是一个学习Cython的好机会。
这个方法依赖于这样一个事实:使用 Cython.Distutils.build_ext
来构建 .pyx
文件时(至少在Cython 0.14版本中),总是会在和源 .pyx
文件同一个目录下生成一个 .c
文件。
下面是一个简化版的 setup.py
,我希望能展示出其中的要点:
from distutils.core import setup
from distutils.extension import Extension
try:
from Cython.Distutils import build_ext
except ImportError:
use_cython = False
else:
use_cython = True
cmdclass = {}
ext_modules = []
if use_cython:
ext_modules += [
Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.pyx"]),
]
cmdclass.update({'build_ext': build_ext})
else:
ext_modules += [
Extension("mypackage.mycythonmodule", ["cython/mycythonmodule.c"]),
]
setup(
name='mypackage',
...
cmdclass=cmdclass,
ext_modules=ext_modules,
...
)
我还编辑了 MANIFEST.in
文件,以确保 mycythonmodule.c
被包含在源代码分发中(这个源代码分发是通过 python setup.py sdist
创建的):
...
recursive-include cython *
...
我不会把 mycythonmodule.c
提交到版本控制的主干(对于Mercurial来说是'默认')。当我发布新版本时,我需要记得先运行 python setup.py build_ext
,以确保 mycythonmodule.c
是最新的,并且包含在源代码分发中。我还会创建一个发布分支,并把C文件提交到这个分支。这样我就有了一个历史记录,记录了与该版本一起分发的C文件。