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

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

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

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

Как мне структурировать файлы в пакете, чтобы удовлетворить оба эти сценария?

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

108 голосов | спросил Craig McQueen 22 WedEurope/Moscow2010-12-22T05:44:31+03:00Europe/Moscow12bEurope/MoscowWed, 22 Dec 2010 05:44:31 +0300 2010, 05:44:31

9 ответов


0

Я сделал это сам сейчас, в пакете Python simplerandom ( Репозиторий BitBucket - РЕДАКТИРОВАТЬ: сейчас github ) (я не ожидаю, что это будет популярный пакет, но это был хороший шанс узнать Cython).

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

Вот урезанная версия 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, который был распространен с этим выпуском.

ответил Craig McQueen 23 ThuEurope/Moscow2010-12-23T04:58:32+03:00Europe/Moscow12bEurope/MoscowThu, 23 Dec 2010 04:58:32 +0300 2010, 04:58:32
0

Добавление к ответу Крейга МакКуина: см. ниже о том, как переопределить команду 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
ответил kynan 24 PM00000040000001831 2013, 16:24:18
0

http://docs.cython.org/src/reference/compilation.html # раздаточный-Cython-модули

  

Настоятельно рекомендуется распространять сгенерированные файлы .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"])]
)
ответил Colonel Panic 2 +04002013-10-02T17:28:24+04:00312013bEurope/MoscowWed, 02 Oct 2013 17:28:24 +0400 2013, 17:28:24
0

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

В противном случае вам нужна специальная команда build_ext для distutils, которая сначала создает файл C. Cython уже включает в себя один. 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

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

ответил Lennart Regebro 22 WedEurope/Moscow2010-12-22T10:30:01+03:00Europe/Moscow12bEurope/MoscowWed, 22 Dec 2010 10:30:01 +0300 2010, 10:30:01
0

Включать (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

ответил McKelvin 11 ThuEurope/Moscow2014-12-11T13:23:56+03:00Europe/Moscow12bEurope/MoscowThu, 11 Dec 2014 13:23:56 +0300 2014, 13:23:56
0

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

Givig структура, как это:

__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

Удачной компиляции;)

ответил zzart 24 J0000006Europe/Moscow 2014, 17:33:11
0

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

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, если он не может быть импортирован. Вероятно, не стоит делиться этим кодом, но для моих собственных зависимостей он достаточно хорош.

ответил kay 18 Mayam16 2016, 04:01:17
0

Самый простой способ, который я нашел, используя только setuptools вместо 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)
ответил MosteM 26 Jpm1000000pmFri, 26 Jan 2018 16:48:28 +030018 2018, 16:48:28
0

Все остальные ответы либо основаны на

  • distutils
  • импорт из Cython.Build, из-за которого возникает проблема, связанная с запросом Cython через 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'],
        ),
    ],
)
ответил bluenote10 30 J000000Monday18 2018, 14:29:46

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132