【问题标题】:Compiling an optional cython extension only when possible in setup.py仅在 setup.py 中可能时编译可选的 cython 扩展
【发布时间】:2017-06-06 07:09:48
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: python cython distutils setup.py distutils2


    【解决方案1】:

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

    假设您的包的名称将是“模块”,并且您有一个功能,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 版本,否则使用纯 python 版本。它假定subc_sub 将提供相同的API。

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

    【讨论】:

    • 对于我的情况,我可以删除最后一个异常块中的两个 if-cases,但我不得不改用del setup_args['ext_modules']。你能解释一下你的代码在做什么吗?
    【解决方案2】:

    Extension在构造函数中有参数optional

    可选 - 指定扩展中的构建失败不应该 中止构建过程,但只是跳过扩展。

    这里也是linkmgc提出的一段相当有趣的代码历史。

    【讨论】:

      【解决方案3】:

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

      是相关的,问题是如何从 Cython 回退到“已经生成的 C 代码”。您可以使用类似的策略来选择要安装.py.pyx 代码中的哪一个。

      【讨论】:

      • 我看不出这是如何适用的。他们尝试将 cython 作为 python 模块导入,如果导入失败则回退到 C 模块。您如何建议我尝试在 python 中导入系统 C 编译器?
      • 确实,我下结论有点早了。
      猜你喜欢
      • 2020-07-27
      • 1970-01-01
      • 2015-01-09
      • 2018-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-25
      • 2013-02-07
      相关资源
      最近更新 更多