Подтвердить что ты не робот

Распространение общей библиотеки и некоторых C-кода с помощью модуля расширения Cython

Я пытаюсь взять некоторые функции из большой общей библиотеки С++ (libbig.so) и выставить их на Python через Cython. Для этого у меня есть небольшой файл на С++ (small.cpp), который предоставляет тонкую оболочку вокруг функциональности из разделяемой библиотеки, которая мне нужна, таким образом, чтобы она легко вызывалась через Cython (pysmall.pyx).

libbig.so → small.cpp, small.h → libsmall.so → pysmall.pyx → pysmall.cpp → pysmall.so

Я могу создать и запустить этот модуль расширения на своем собственном компьютере: я просто компилирую small.cpp в libsmall.so, а затем говорю "libraries = ['small']" в объекте Extension в setup.py для создания модуль расширения pysmall.so.

Теперь я пытаюсь распространять этот модуль расширения, и мне сложно отслеживать ресурсы, которые описывают оптимальные методы setup.py для распространения модуля Cython, а также исходные и разделяемые библиотеки C. Я прочитал "Установка модулей Python", "Распространение модулей Python" и "Распространение Cython модулей". Я понимаю, как распространять модуль расширения самостоятельно. Я менее уверен в наилучшем способе распространения зависимостей модуля расширения.

Документация Cython указывает, что вы должны включить сгенерированные файлы .cpp, а также файлы .pyx, если Cython отсутствует, но он не предоставляет код, чтобы продемонстрировать, как лучше всего обрабатывать каждую ситуацию. В нем также не упоминается, как распределять общие библиотеки, от которых зависит модуль Cython.

Я копаю скрипты setup.py из pandas, lxml, pyzmq, h5py и т.д., и там довольно много посторонней работы. Если у кого-то есть указатели или пример кода, которые могли бы ускорить этот процесс, я бы, конечно же, его оценил!

4b9b3361

Ответ 1

1) Распространение libbig.so

Это проблема, с которой python не поможет вам. К кому вы ориентируетесь? Если это Linux, можете ли вы попросить их установить его с помощью менеджера пакетов? Если libbig не распространяется через диспетчер пакетов или это не linux, и вы ориентируетесь на несколько архитектур, возможно, вам придется распространять источник libbig.

2) Cython/setuptools.

Честно говоря, я думаю, что его проще всего просто потребовать, чтобы у людей был Киот. Таким образом, существует только одна основная версия кода, и вам не нужно беспокоиться о несоответствиях между кодом .pyx и .cpp. Самый простой способ сделать это - использовать setuptools вместо distutils. Таким образом, вы можете использовать:

setup('mypackage',
    ...
    install_requires=['cython'])

В целом ваш setup.py script будет выглядеть примерно так:

# setup.py

from setuptools import setup, Extension
from Cython.Distutils import build_ext

pysmall = Extension('pysmall',
    sources = ['pysmall.pyx', 'small.cpp'],
    include_dirs = ['include/'])

setup(name='mypackage',
      packages=['yourpurepythonpackage'],
      install_requires=['cython==0.17'],
      ext_modules=[pysmall],
      cmdclass = {'build_ext': build_ext})

Если вам не нравится идея использования cython, вы можете сделать что-то вроде:

# setup.py

import warnings
try:
    from Cython.Distutils import build_ext
    from setuptools import setup, Extension
    HAVE_CYTHON = True
except ImportError as e:
    HAVE_CYTHON = False
    warnings.warn(e.message)
    from distutils.core import setup, Extension
    from distutils.command import build_ext

pysmall = Extension('pysmall',
    sources = ['pysmall.pyx', 'small.cpp'],
    include_dirs = ['include/'])

configuration = {'name': 'mypackage',
      'packages': ['yourpurepythonpackage'],
      'install_requires': ['cython==0.17'],
      'ext_modules': [pysmall],
      'cmdclass': {'build_ext': build_ext}}

if not HAVE_CYTHON:
    pysmall.sources[0] = 'pysmall.cpp'
    configuration.pop('install_requires')

setup(**configuration)

Ответ 2

Вот мое сложное решение. Идея состоит в том, чтобы "скрыть" наличие cython до тех пор, пока оно не будет установлено требованиями. Это может быть достигнуто ленивой оценкой. Вот пример:

from setuptools import setup, Extension

class lazy_cythonize(list):
    def __init__(self, callback):
        self._list, self.callback = None, callback
    def c_list(self):
        if self._list is None: self._list = self.callback()
        return self._list
    def __iter__(self):
        for e in self.c_list(): yield e
    def __getitem__(self, ii): return self.c_list()[ii]
    def __len__(self): return len(self.c_list())

def extensions():
    from Cython.Build import cythonize
    ext = Extension('native_ext_name', ['your/src/*.pyx'])
    return cythonize([ext])


configuration = {
    'name': 'mypackage',
    'packages': ['yourpurepythonpackage'],
    'install_requires': ['cython==0.17'],
    'ext_modules': lazy_cythonize(extensions)
}

setup(**configuration)

lazy_cythonize - это поддельный список, который генерирует свои внутренние элементы только тогда, когда кто-то пытается получить к нему доступ.
Когда это потребуется, этот класс импортирует Cython.Build и генерирует список расширений. Это позволяет сохранить файлы *.c в вашем проекте, требуя установки cython при создании модуля.

Довольно сложно, но на самом деле он работает.

Ответ 3

Я поставил исправление для setuptools 288, намеченного для выпуска в качестве setuptools 18.0. Эта запись в журнале изменений описывает технику, которая должна работать с этой сборкой. Для тестирования доступен .