我有一个完全在python中实现的python模块。(出于便携性原因。)
cython模块中已复制了一小部分的实现。在可能的情况下提高性能。
我知道如何使用来安装.c
cython创建的模块distutils
。但是,如果一台机器没有安装编译器,我怀疑即使该模块在纯python模式下仍然可用,安装也会失败。
有没有办法编译该.c
模块(如果可能的话),但无法正常运行,如果无法编译,则在没有该模块的情况下进行安装?
我想您将不得不在模块setup.py
中的一个__init__
文件中进行一些修改。
假设包的名称为“ module”,并且您具有一个功能,sub
为此,您在sub
子文件夹中具有纯python代码,在子文件夹中具有等效的C代码c_sub
。例如在您的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) class BuildFailed(Exception): pass def construct_build_ext(build_ext): class WrappedBuildExt(build_ext): # This class allows C extension building to fail. def run(self): try: build_ext.run(self) except DistutilsPlatformError as x: raise BuildFailed(x) def build_extension(self, ext): try: build_ext.build_extension(self, ext) except ext_errors as x: raise BuildFailed(x) return WrappedBuildExt setup_args = {'name': 'module', 'license': 'BSD', 'author': 'xxx', 'packages': ['module', 'module.sub', 'module.c_sub'], 'cmdclass': {} } ext_modules = [Extension("module.c_sub._sub", ["module/c_sub/_sub.c"])] cmd_classes = setup_args.setdefault('cmdclass', {}) try: # try building with c code : setup_args['cmdclass']['build_ext'] = construct_build_ext(build_ext) setup(ext_modules=ext_modules, **setup_args) except BuildFailed 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 'build_ext' in cmd_classes: del cmd_classes['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版本,则将使用C版本,否则将使用普通的python版本。它假定sub
并且c_sub
将提供相同的API。
您可以在软件包中找到执行此操作的安装文件示例Shapely
。实际上,我发布的大多数代码都是construct_build_ext
从此文件复制(函数)或改编(后一行)的。