web-dev-qa-db-ja.com

Cythonコードを含むPythonパッケージをどのように構築すればよいですか

いくつかの Cython コードを含むPythonパッケージを作成したい。Cythonコードがうまく機能している。それをパッケージ化します。

パッケージをインストールするだけのほとんどの人のために、.c Cythonが作成し、setup.pyをコンパイルしてモジュールを生成します。その場合、ユーザーはパッケージをインストールするためにCythonをインストールする必要はありません。

しかし、パッケージを変更したい人のために、Cython .pyxファイル、および何らかの理由でsetup.py Cythonを使用してそれらをビルドします(したがって、それらのユーザーはCythonをインストールする必要があります

これらの両方のシナリオに対応するために、パッケージ内のファイルをどのように構成する必要がありますか?

Cythonのドキュメントに少しガイダンスがあります 。しかし、単一のsetup.py Cythonの場合となしの両方のケースを処理します。

117
Craig McQueen

Python package simplerandomBitBucket repo -EDIT:now- github )(これが人気のあるパッケージになるとは思いませんが、Cythonを学ぶ良い機会でした)。

この方法は、(少なくともCythonバージョン0.14で).pyxを使用してCython.Distutils.build_extファイルをビルドすると、常にソース.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'(またはMercurialの 'default')にコミットしません。リリースを行うときは、最初にpython setup.py build_extを実行し、mycythonmodule.cが存在し、ソースコード配布用に最新であることを確認する必要があります。また、リリースブランチを作成し、Cファイルをブランチにコミットします。そうすれば、そのリリースで配布されたCファイルの履歴レコードがあります。

66
Craig McQueen

Craig McQueenの答えに追加: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
19
kynan

http://docs.cython.org/en/latest/src/userguide/source_files_and_compilation.html#distributing-cython-modules

生成された.cファイルとCythonソースを配布することを強くお勧めします。これにより、ユーザーはCythonを使用可能にする必要なくモジュールをインストールできます。

また、配布するバージョンでCythonコンパイルをデフォルトで有効にしないことをお勧めします。ユーザーがCythonをインストールしていても、おそらくモジュールのインストールだけに使用したくないでしょう。また、彼が持っているバージョンはあなたが使用したものとは異なるかもしれず、ソースを正しくコンパイルできないかもしれません。

これは単に、出荷するsetup.pyファイルが、生成された.cファイルの通常のdistutilsファイルであることを意味します。代わりに、基本的な例があります。

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

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

最も簡単なのは両方を含めることですが、Cファイルを使用するだけですか? .pyxファイルを含めるのはいいことですが、.cファイルを作成した後は、必要ありません。 .pyxを再コンパイルしたい人は、Pyrexをインストールして手動で実行できます。

それ以外の場合は、最初にCファイルをビルドするdistutils用のカスタムbuild_extコマンドが必要です。 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

それを処理する必要があります。

7
Lennart Regebro

(Cython)生成された.cファイルを含めるのはかなり奇妙です。特にgitに含める場合。 setuptools_cython を使用したいと思います。 Cythonが利用できない場合、Cython環境が組み込まれたEggをビルドし、Eggを使用してコードをビルドします。

可能な例: https://github.com/douban/greenify/blob/master/setup.py


アップデート(2017-01-05):

setuptools 18.0なので、setuptools_cythonを使用する必要はありません。 ここ は、setuptools_cythonなしでCythonプロジェクトをゼロからビルドする例です。

4
McKelvin

私が思いついた簡単なハック:

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をインポートできなかった場合はインストールしてください。おそらくこのコードを共有すべきではありませんが、私自身の依存関係については十分です。

2
kay

これは、ビルド内にネストされたディレクトリを簡単に含めることができるように作成したセットアップスクリプトです。パッケージ内のフォルダーから実行する必要があります。

このような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

ハッピーコンパイル;)

2
zzart

機能制限されたdistutilsの代わりにsetuptoolsのみを使用して見つけた最も簡単な方法は

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)
1
MosteM

他のすべての答えは、

  • distutils
  • Cython.Buildからインポートすると、setup_requiresを介してcythonを要求してからインポートするまでの間に鶏肉と卵の問題が発生します。

最新の解決策は、代わりにsetuptoolsを使用することです。 この回答 を参照してください(Cython拡張機能の自動処理にはsetuptools 18.0が必要です。つまり、すでに長年利用可能です)。要件処理、エントリポイント、cythonモジュールを備えた最新の標準setup.pyは次のようになります。

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'],
        ),
    ],
)
1
bluenote10