Cython を利用するための setup.py の書き方

Cython のコードを拡張モジュールとしてパッケージに組み込むには setup.py に変更を加える必要があります.

distutils を用いる方法

distutils を用いる方法は公式ドキュメント (英語 / 日本語) に掲載されています.

hello.pyx という Cython コードを作成し, 以下のように setup.py を記述すると自動的に Cython による C コードへのコンパイルと C コードのコンパイルが行われます.

from distutils.core import setup
from Cython.Build import cythonize

setup(
    name = 'Sample App',
    ext_modules = cythonize("hello.pyx"),
)

これによってインストールされたモジュールは import hello とすれば import できます.

setuptools を用いる方法

現在 Python のパッケージングは setuptools で行うことが一般的なため, setuptools を用いる方法を紹介します.

ここでは mypackage という名前でパッケージを作成し, mypackage/__init__.py と mypackage/hello.pyx を作成することとします. setup.py は以下のようになります.

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

ext_modules = [
    Extension('mypackage.hello', sources=['mypackage/hello.pyx'])
]

setup(
    name='Sample App',
    packages=['mypackage'],
    ext_modules=ext_modules,
    cmdclass={'build_ext': build_ext}
)

これによってインストールされたモジュールは from mypackage import hello とすれば import できます.

C コンパイラのオプションの設定

Cython で生成した生成した C のコードをコンパイルする際に, インクルードパスやコンパイラオプションを設定したい場合があります. 上の setuptools の例で, Extension の部分を編集することでこれが可能になります. setup.py は以下のようになります.

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

ext_modules = [
    Extension('mypackage.hello',
              sources=['mypackage/hello.pyx'],
              include_dirs=['DIR_1', 'DIR_2'],
              extra_compile_args=['--option-a', '-O3'])
]

setup(
    name='Sample App',
    packages=['mypackage'],
    ext_modules=ext_modules,
    cmdclass={'build_ext': build_ext}
)

インクルードパスやコンパイラオプションはリストで指定することに注意してください. また, コンパイラオプションはデフォルトで設定されるものもあるため, 注意して設定する必要があります.

ちなみに Numpy のインクルードパスは numpy.get_include() で取得できます.

C コードと Cython コードを自動的に選択する

パッケージを配布する際に Cython のコードと Cython で C に変換したコードの両方を配布すれば, Cython をインストールしていないユーザーでもパッケージをインストールできるようになります. このため Cython のコードを配布する際は C のコードもともに配布するようにしたほうがよさそうです.

C のコードと Cython のコードを共に配布する場合, setup.py でどちらのコードからインストールするかを選択する必要があります. mypackage/__init__.py と mypackage/hello.pyx, mypackage/hello.c というファイル構成として作成した setup.py は以下のようになります.

from setuptools import setup, Extension

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

if USE_CYTHON:
    ext = '.pyx'
     cmdclass = {'build_ext': build_ext}
else:
    ext = '.c'
    cmdclass = {}

ext_modules = [
Extension(
    'mypackage.hello',
    sources=['mypackage/hello' + ext])
]

setup(
    name='Sample',
    packages=['mypackage'],
    ext_modules=ext_modules,
    cmdclass=cmdclass
)

はじめに try 文で Cython を読み込めるか判定し, それによってフラグを設定しておきます. そのフラグをもとに拡張子等を設定するようにしています.