仅在 setup.py 中可能时才编译可选的 cython 扩展

Compiling an optional cython extension only when possible in setup.py

我有一个 python 模块在 python 中完全实现。 (出于便携性原因。)

一小部分的实现已在 cython 模块中重复。尽可能提高性能。

我知道如何使用 distutils 安装由 cython 创建的 .c 模块。但是,如果一台机器没有安装编译器,我怀疑即使模块在纯 python 模式下仍然可用,安装也会失败。

有没有办法在可能的情况下编译 .c 模块,但如果无法编译则优雅地失败并在没有它的情况下安装?

问题How should I structure a Python package that contains Cython code

相关,问题是如何从 Cython 回退到 "already generated C code"。您可以使用与 select 类似的策略来安装 .py.pyx 代码。

我想您必须在 setup.py 和模块中的一个 __init__ 文件中进行一些修改。

假设你的包的名称是 "module" 并且你有一个功能,sub 你在 sub 子文件夹中有纯 python 代码,并且c_sub 子文件夹中的等效 C 代码。 例如在你的 setup.py 中:

import logging
from setuptools.extension import Extension
from setuptools.command.build_ext import build_ext
from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError

logging.basicConfig()
log = logging.getLogger(__file__)

ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError, SystemExit)

setup_args = {'name': 'module', 'license': 'BSD', 'author': 'xxx',
    'packages': ['module', 'module.sub', 'module.c_sub'],
    'cmdclass': {'build_ext': build_ext}
    }

ext_modules = [Extension("module.c_sub._sub", ["module/c_sub/_sub.c"])]

try:
    # try building with c code :
    setup(ext_modules=ext_modules, **setup_args)
except ext_errors as ex:
    log.warn(ex)
    log.warn("The C extension could not be compiled")

    ## Retry to install the module without C extensions :
    # Remove any previously defined build_ext command class.
    if 'build_ext' in setup_args['cmdclass']:
        del setup_args['cmdclass']['build_ext']

    # If this new 'setup' call don't fail, the module 
    # will be successfully installed, without the C extension :
    setup(**setup_args)
    log.info("Plain-Python installation succeeded.")

现在您需要在 __init__.py 文件中(或与您的情况相关的任何地方)包含类似的内容:

try:
    from .c_sub import *
except ImportError:
    from .sub import *

通过这种方式,如果构建,将使用 C 版本,other-wise 使用普通 python 版本。它假定 subc_sub 将提供相同的 API.

您可以在 Shapely 包中找到这样做的 example of setup file。实际上,我发布的大部分代码都是从该文件中复制(construct_build_ext 函数)或改编(之后的行)。

Class Extension 在构造函数中有参数 optional:

optional - specifies that a build failure in the extension should not abort the build process, but simply skip the extension.

这里还有一个link to the quite interesting history of piece of code proposed by mgc.