我想制作一个包含一些Cython代码的Python包。我已经很好地运行了Cython代码。但是,现在我想知道如何最好地打包它。
对于大多数只想安装软件包的人,我想要包含Cython创建的.c
文件,并安排setup.py
编译它以生成模块。然后用户不需要安装Cython来安装软件包。
但是对于那些可能想要修改包的人,我也想提供Cython .pyx
文件,并且不知何故也允许setup.py
使用Cython构建它们(所以这些用户< em>将需要安装Cython。)
我应该如何构建包中的文件以满足这两种情况?
Cython documentation gives a little guidance。但它没有说明如何制作一个setup.py
来处理有/无Cython案例。
答案 0 :(得分:60)
我现在已经在Python包中simplerandom
(BitBucket repo - 编辑:现在github)自己完成了这个(我不认为这是一个受欢迎的包,但是这是学习Cython的好机会。)
此方法依赖于以下事实:使用.pyx
(至少使用Cython版本0.14)构建Cython.Distutils.build_ext
文件似乎始终在与源相同的目录中创建.c
文件.pyx
档案。
以下是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
提交给版本控制“trunk”(或Mercurial的“默认”)。当我发布版本时,我需要首先记住python setup.py build_ext
,以确保mycythonmodule.c
存在并且是源代码分发的最新版本。我还创建了一个发布分支,并将C文件提交到分支中。这样我就可以获得随该版本一起发布的C文件的历史记录。
答案 1 :(得分:18)
添加到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
答案 2 :(得分:16)
强烈建议您分发生成的.c文件以及Cython源代码,以便用户可以安装模块而无需使用Cython。
还建议在您分发的版本中默认不启用Cython编译。即使用户安装了Cython,他也可能不想仅仅使用它来安装模块。此外,他所拥有的版本可能与您使用的版本不同,并且可能无法正确编译您的源代码。
这只是意味着您附带的setup.py文件将只是生成的.c文件中的正常distutils文件,对于我们将要使用的基本示例:
from distutils.core import setup from distutils.extension import Extension setup( ext_modules = [Extension("example", ["example.c"])] )
答案 3 :(得分:7)
最简单的是包含两者但只使用c文件?包含.pyx文件很不错,但是无论如何都不需要.c文件。想要重新编译.pyx的人可以安装Pyrex并手动完成。
否则,您需要为distutils安装自定义build_ext命令,以便首先构建C文件。 Cython已经包含了一个。 http://docs.cython.org/src/userguide/source_files_and_compilation.html
该文档没有做的是如何使这个条件,但
try:
from Cython.distutils import build_ext
except ImportError:
from distutils.command import build_ext
应该处理它。
答案 4 :(得分:4)
包含(Cython)生成的.c文件非常奇怪。特别是当我们在git中包含它时。我更喜欢使用setuptools_cython。当Cython不可用时,它将构建一个内置Cython环境的egg,然后使用egg来构建代码。
一个可能的例子:https://github.com/douban/greenify/blob/master/setup.py
更新(2017年1月5日):
自setuptools 18.0
以来,无需使用setuptools_cython
。 Here是一个从头开始构建Cython项目的示例,没有setuptools_cython
。
答案 5 :(得分:2)
这是我编写的一个安装脚本,它可以更容易地在构建中包含嵌套目录。需要从包中的文件夹运行它。
Givig这样的结构:
__init__.py
setup.py
test.py
subdir/
__init__.py
anothertest.py
setup.py
from setuptools import setup, Extension
from Cython.Distutils import build_ext
# from os import path
ext_names = (
'test',
'subdir.anothertest',
)
cmdclass = {'build_ext': build_ext}
# for modules in main dir
ext_modules = [
Extension(
ext,
[ext + ".py"],
)
for ext in ext_names if ext.find('.') < 0]
# for modules in subdir ONLY ONE LEVEL DOWN!!
# modify it if you need more !!!
ext_modules += [
Extension(
ext,
["/".join(ext.split('.')) + ".py"],
)
for ext in ext_names if ext.find('.') > 0]
setup(
name='name',
ext_modules=ext_modules,
cmdclass=cmdclass,
packages=["base", "base.subdir"],
)
# Build --------------------------
# python setup.py build_ext --inplace
快乐编译;)
答案 6 :(得分:2)
我想出的简单黑客:
from distutils.core import setup
try:
from Cython.Build import cythonize
except ImportError:
from pip import pip
pip.main(['install', 'cython'])
from Cython.Build import cythonize
setup(…)
如果无法导入Cython,请立即安装。一个人可能不应该共享这个代码,但对于我自己的依赖,这已经足够了。
答案 7 :(得分:1)
我发现仅使用setuptools而不是功能受限的distutils的最简单方法是
from setuptools import setup
from setuptools.extension import Extension
try:
from Cython.Build import cythonize
except ImportError:
use_cython = False
else:
use_cython = True
ext_modules = []
if use_cython:
ext_modules += cythonize('package/cython_module.pyx')
else:
ext_modules += [Extension('package.cython_module',
['package/cython_modules.c'])]
setup(name='package_name', ext_modules=ext_modules)
答案 8 :(得分:1)
所有其他答案都依赖
Cython.Build
导入,这会在通过setup_requires
要求cython并将其导入之间造成鸡到蛋的问题。一种现代的解决方案是改为使用setuptools,请参见this answer(自动处理Cython扩展程序需要setuptools 18.0,即它已经可用了很多年)。具有需求处理,入口点和cython模块的现代标准setup.py
看起来像这样:
from setuptools import setup, Extension
with open('requirements.txt') as f:
requirements = f.read().splitlines()
setup(
name='MyPackage',
install_requires=requirements,
setup_requires=[
'setuptools>=18.0', # automatically handles Cython extensions
'cython>=0.28.4',
],
entry_points={
'console_scripts': [
'mymain = mypackage.main:main',
],
},
ext_modules=[
Extension(
'mypackage.my_cython_module',
sources=['mypackage/my_cython_module.pyx'],
),
],
)