Как я должен структурировать пакет Python, содержащий код Cython



Я хотел бы сделать пакет Python, содержащий некоторые на Cython код. У меня есть код Цитона работает хорошо. Однако теперь я хочу знать, как лучше всего упаковать его.



для большинства людей, которые просто хотят установить пакет, хочу включить .c файл, который создает Cython, и организовать для setup.py чтобы скомпилировать это для создания модуля. Тогда пользователю не нужно устанавливать Cython для установки пакета.



но для людей, которые могут хотите изменить пакет, я также хотел бы предоставить Cython .pyx файлы, и как-то также позволяют setup.py чтобы построить их с помощью Cython (так что эти пользователи б нужно установить Cython).



как я должен структурировать файлы в пакете для удовлетворения обоих этих сценариев?



The документация Cython дает небольшое руководство. Но он не говорит, Как сделать один setup.py который обрабатывает оба случая с / без Cython.

569   9  

9 ответов:

я сделал это сам сейчас, в пакете Python simplerandom (BitBucket repo - EDIT: сейчас github) (я не ожидаю, что это будет популярный пакет, но это был хороший шанс узнать на Cython).

этот метод основан на том, что при строительстве С Cython.Distutils.build_ext (по крайней мере, с Cython версии 0.14) всегда кажется, чтобы создать .c файл в том же каталоге, что и исходный .

здесь урезанная версия setup.py который, я надеюсь, показывает основы:

from distutils.core import setup
from distutils.extension import Extension

try:
    from Cython.Distutils import build_ext
except ImportError:
    use_cython = False
else:
    use_cython = True

cmdclass = { }
ext_modules = [ ]

if use_cython:
    ext_modules += [
        Extension("mypackage.mycythonmodule", [ "cython/mycythonmodule.pyx" ]),
    ]
    cmdclass.update({ 'build_ext': build_ext })
else:
    ext_modules += [
        Extension("mypackage.mycythonmodule", [ "cython/mycythonmodule.c" ]),
    ]

setup(
    name='mypackage',
    ...
    cmdclass = cmdclass,
    ext_modules=ext_modules,
    ...
)

я также редактировал MANIFEST.in обеспечить mycythonmodule.c входит в исходный дистрибутив (исходный дистрибутив, созданный с помощью python setup.py sdist):

...
recursive-include cython *
...

я не совершал mycythonmodule.c для управления версиями ' trunk '(или' default ' для Mercurial). Когда я делаю релиз, мне нужно помнить, чтобы сделать python setup.py build_ext во-первых, чтобы гарантировать, что mycythonmodule.c присутствует и обновляется для распространения исходного кода. Я также сделайте ветку выпуска и зафиксируйте файл C в ветке. Таким образом, у меня есть историческая запись файла C, который был распространен с этим выпуском.

добавление к ответу Крейга Маккуина: смотрите ниже, как переопределить sdist команда, чтобы Cython автоматически компилировал исходные файлы перед созданием исходного дистрибутива.

таким образом, вы не рискуете случайно распространять устаревшие C источники. Это также помогает в случае, когда у вас есть ограниченный контроль над процессом распределения, например, при автоматическом создании распределений из непрерывной интеграции и т. д.

from distutils.command.sdist import sdist as _sdist

...

class sdist(_sdist):
    def run(self):
        # Make sure the compiled Cython files in the distribution are up-to-date
        from Cython.Build import cythonize
        cythonize(['cython/mycythonmodule.pyx'])
        _sdist.run(self)
cmdclass['sdist'] = sdist

http://docs.cython.org/src/reference/compilation.html#distributing-cython-modules

настоятельно рекомендуется распространять сгенерированные данные .c файлы, а также ваши источники Cython, так что пользователи могут установить модуль без необходимости иметь Cython доступны.

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

Это просто означает, что setup.py файл, с которым вы отправляете, будет просто обычным файлом distutils на сгенерированном .c файлы, для основного примера мы бы вместо этого:

from distutils.core import setup
from distutils.extension import Extension

setup(
    ext_modules = [Extension("example", ["example.c"])]
)

проще всего включить оба, но просто использовать c-файл? В том числе .файл pyx хорош, но он не нужен, как только у вас есть .c файл в любом случае. Люди, которые хотят перекомпилировать .pyx может установить Pyrex и сделать это вручную.

в противном случае вам нужно иметь пользовательскую команду build_ext для distutils, которая сначала создает файл C. Цитон уже включает в себя один. http://docs.cython.org/src/userguide/source_files_and_compilation.html

что документация не говорит, Как сделать это условным, но

try:
     from Cython.distutils import build_ext
except ImportError:
     from distutils.command import build_ext

должны справиться с этим.

в том числе (Cython) генерируется .файлы c довольно странно. Особенно когда мы включаем это в git. Я бы предпочел использовать setuptools_cython. Когда Cython недоступен, он построит яйцо, которое имеет встроенную среду Cython, а затем построит ваш код с помощью яйца.

возможный пример:https://github.com/douban/greenify/blob/master/setup.py


обновление(2017-01-05):

С setuptools 18.0, нет необходимости используйте setuptools_cython. здесь является примером для создания проекта Cython с нуля без setuptools_cython.

это сценарий установки, который я написал, что облегчает включение вложенных каталогов внутри сборки. Нужно запустить его из папки в пакете.

структура дающими такой:

__init__.py
setup.py
test.py
subdir/
      __init__.py
      anothertest.py

setup.py

from setuptools import setup, Extension
from Cython.Distutils import build_ext
# from os import path
ext_names = (
    'test',
    'subdir.anothertest',       
) 

cmdclass = {'build_ext': build_ext}
# for modules in main dir      
ext_modules = [
    Extension(
        ext,
        [ext + ".py"],            
    ) 
    for ext in ext_names if ext.find('.') < 0] 
# for modules in subdir ONLY ONE LEVEL DOWN!! 
# modify it if you need more !!!
ext_modules += [
    Extension(
        ext,
        ["/".join(ext.split('.')) + ".py"],     
    )
    for ext in ext_names if ext.find('.') > 0]

setup(
    name='name',
    ext_modules=ext_modules,
    cmdclass=cmdclass,
    packages=["base", "base.subdir"],
)
#  Build --------------------------
#  python setup.py build_ext --inplace

счастливый компиляции ;)

простой хак, который я придумал:

from distutils.core import setup

try:
    from Cython.Build import cythonize
except ImportError:
    from pip import pip

    pip.main(['install', 'cython'])

    from Cython.Build import cythonize


setup(…)

просто установите Cython, если он не может быть импортирован. Вероятно, не следует делиться этим кодом, но для моих собственных зависимостей это достаточно хорошо.

самый простой способ, который я нашел, используя только setuptools вместо функции limited distutils-это

from setuptools import setup
from setuptools.extension import Extension
try:
    from Cython.Build import cythonize
except ImportError:
    use_cython = False
else:
    use_cython = True

ext_modules = []
if use_cython:
    ext_modules += cythonize('package/cython_module.pyx')
else:
    ext_modules += [Extension('package.cython_module',
                              ['package/cython_modules.c'])]

setup(name='package_name', ext_modules=ext_modules)

все остальные ответы либо положиться на

  • distutils
  • импорт из Cython.Build, что создает проблему курицы и яйца между требованием цитона через setup_requires и импортировать его.

современное решение-это setuptools вместо этого, см. ответ (для автоматической обработки расширений Cython требуется setuptools 18.0, т. е. он доступен уже много лет). Современный стандарт setup.py с обработкой требований, запись точка, и модуль cython может выглядеть так:

from setuptools import setup, Extension

with open('requirements.txt') as f:
    requirements = f.read().splitlines()

setup(
    name='MyPackage',
    install_requires=requirements,
    setup_requires=[
        'setuptools>=18.0',  # automatically handles Cython extensions
        'cython>=0.28.4',
    ],
    entry_points={
        'console_scripts': [
            'mymain = mypackage.main:main',
        ],
    },
    ext_modules=[
        Extension(
            'mypackage.my_cython_module',
            sources=['mypackage/my_cython_module.pyx'],
        ),
    ],
)

Comments

    Ничего не найдено.