2017-01-21 18 views
2

У меня есть модуль python, полностью реализованный в python. (По причинам мобильности.)Компиляция дополнительного расширения cython только по возможности в setup.py

Реализация небольшой части была дублирована в модуле cython. Чтобы улучшить производительность, где это возможно.

Я знаю, как установить модули .c, созданные cython с помощью distutils. Однако, если у машины нет установленного компилятора, я подозреваю, что установка завершится неудачно, хотя модуль все еще используется в чистом режиме python.

Есть ли способ скомпилировать модуль .c, если это возможно, но изящно выполнить его и установить без него, если компиляция невозможна?

ответ

0

Вопрос How should I structure a Python package that contains Cython code

связано, есть вопрос в том, как Откат от Cython к «уже сгенерированного кода C». Вы можете использовать аналогичную стратегию, чтобы выбрать, какой из .py или код .pyx для установки.

+0

Я не вижу, как это применимо. Они пытаются импортировать cython в качестве модуля python и возвращаться к C-модулю, если импорт не выполняется. Как вы предлагаете мне попробовать импортировать системный компилятор C в python? – ARF

+0

Действительно, я немного подпрыгнул до выводов. –

2

Я думаю, вам придется внести некоторые изменения как в ваш setup.py, так и в один файл __init__ в ваш модуль.

Пусть говорят имя пакета будет «модуль» и у вас есть функциональность, sub, для которых у вас есть чистый код питона в sub вложенной и эквивалентный код 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 версия будет использоваться, если она была построена, в противном случае используется простая версия python. Предполагается, что sub и c_sub предоставят тот же API.

Вы можете найти example of setup file таким образом в пакете Shapely. Фактически большая часть кода, который я опубликовал, была скопирована (функция construct_build_ext) или адаптирована (строки после) из этого файла.