我在python中完全实现了一个python模块。 (出于便携性原因。)
在cython模块中重复了一小部分的实现。尽可能提高性能。
我知道如何使用.c
安装由cython创建的distutils
模块。但是,如果一台机器没有安装编译器,我怀疑即使该模块仍然可以在纯python模式下使用,安装程序也会失败。
有可能在可能的情况下编译.c
模块,但如果无法进行编译,则可以正常失败并在没有它的情况下安装吗?
答案 0 :(得分:4)
我想您必须在模块中的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)
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版本,否则使用普通python版本。它假定sub
和c_sub
将提供相同的API。
您可以在Shapely
包中找到example of setup file这样做的方式。实际上我发布的大部分代码都是从这个文件中复制(construct_build_ext
函数)或改编(后面的行)。
答案 1 :(得分:1)
答案 2 :(得分:0)
问题How should I structure a Python package that contains Cython code
是相关的,那里的问题是如何从Cython回退到“已经生成的C代码”。您可以使用类似的策略来选择要安装的.py
或.pyx
代码。