ソースファイルとコンパイル

Note

詳しくはリファレンスセクションの コンパイル を参照してください。

Cython のソースファイル名は、モジュールの名前に .pyx 拡張子をつけ たものにします。例えば、 primes という名前のモジュールのソースファ イル名は primes.pyx です。

.pyx ファイルは、二通りの方法で拡張モジュールに変換できます。 一つは、以下のように、 Cython コンパイラを使って手動でコンパイルする方 法です:

$ cython primes.pyx

このコマンドで、 primes.c というファイルが生成されます。次に、 拡張モジュールをコンパイルする際に必要な諸々のオプションを付けて C コ ンパイラを実行して、拡張モジュールを生成します。必要なオプションは Python のドキュメントを参照してください。

もう一つの、おそらくよりベターな方法は、Cython の提供する distutils 拡張を使ってコンパイルする方法です。この方法の利点は、 distutils 拡張が autotools の小型版のように動作して、プラット フォーム固有のコンパイラオプションを決めてくれるところです。

簡単なc setup.py

Cython に付属の distutils 拡張を使うと、 .pyx ファイルを setup ファイルの Extension コンストラクタに直接渡せます。

例えば、 example.pyx という Cython ファイル一つだけをコンパイ ルして拡張モジュールしたいなら、 setup.py は以下のように書きま す:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("example", ["example.pyx"])]
)

setup.py の中身を詳しく理解したければ、 distutils の公 式ドキュメントを参照してください。拡張モジュールをコンパイルして、現在 のディレクトリに出力したければ、以下のように実行します:

$ python setup.py build_ext --inplace

C ファイルに依存関係のある Cython ファイル

C のファイルを Cython でラップしていて、それらを拡張モジュールにコンパ イルしたいのなら、 setup.py ファイルの基本的な構成は以下のよう になります:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

sourcefiles = ['example.pyx', 'helper.c', 'another_helper.c']

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("example", sourcefiles)]
)

ここではファイルのリストに一旦名前をつけています。必須ではないのですが、 ファイルのリストが長くなったときに、読みやすくするためです。

Extension クラスには、様々なオプションを指定できます。使える オプションとその説明は、 distutils のドキュメント を参照してくださ い。外部のライブラリにリンクする場合は、 .h ファイルやライブラリファ イルの場所を指定する必要があるので、 include_dirs, libraries, library_dirs といったオプションのことは知っておいたほうがよいでしょ う。

複数の Cython ファイルを一つのパッケージにまとめる

(執筆中です)

Cython モジュールの配布

Cython モジュールを配布するときは、 Cython で書いたソースコードの他に、 Cython が生成した .c ファイルも配布するよう、強く勧めます。 ユーザが Cython を持っていなくてもモジュールをインストールできるからで す。

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

要するに、 setup.py ファイルは、 Cython が生成した .c ファ イルだけを含む、通常の distutils のファイルにするわけです。例えば、簡 単な例を挙げると、以下のようになるでしょう:

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

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

Pyximport

Cython はコンパイラです。ですから、皆さんは、 Cython のモジュールを作 成するのに、編集・コンパイル・テストのサイクルを繰り返そうとすることで しょう。しかし、個人的には、 Python の実装の根底にある考えには、言語自 体はコンパイル型 (Python のモジュールは .pyc にコンパイルされる) でありながら、コンパイルプロセスは隠蔽して、ユーザがそのことを心配しな くてもよくしていると思うのです。 Pyximport は、この手のことを Cython モジュールに対して実施してくれます。 例えば、ある Cython モジュール foo.pyx を書いたとすると、 Pyximport を使って、このモジュールを普通の Python モジュールのように import できます:

import pyximport; pyximport.install()
import foo

import すると、 foo.pyx がコンパイルされます (コードにエラーが あれば、適切な例外を送出します)。

常に Cython ファイルをビルドせずに import したいなら、上の行を sitecustomize.py に書いておくとよいでしょう。そうすれば、毎回 Python を起動した際に import フックがインストールされるので、 Cython モジュールを import 文だけで使えます。また、 Cython モジュールをテスト する時に、以下のように書けます:

$ python -c "import foo"

依存関係の扱い

Pyximport 1.1 では、モジュールに対して複数の依存ファイル (.h.pxd ファイルなど) を指定できます。Cython モジュール が foo という名前で、 foo.pyx というファイル名の場合には、 foo.pyxdep という名前のファイルを作成します。 モジュール名.pyxdep ファイルには、ファイル名のリストやファイル 名の “glob” (*.pxdinclude/*.h) を列挙します。 ファイル名や glob パターンは、それぞれ 1 行に分けて書きます。 Pyximport は、各依存関係ファイルのタイムスタンプをチェックして、モジュー ルをリビルドすべきか判定します。依存関係をうまく扱うために、 Pyximport は ”.pyx” ソースファイルの最終変更時刻を更新します。将来のバージョンで は、 distutils に依存関係を報告するなど、より洗練された仕組みにする予 定です。

制限

Pyximport には、Cython ファイルのコンパイル方法を制御する手段がありま せん。通常は、デフォルトの設定で充分でしょう。ただ、プログラムを C と Cython の半々で書いて、一つのライブラリにビルドしたい場合には、問題に 突き当たるかもしれません。 Pyximpor 1.2 では、上記のようなビルドに対応 する予定です。

Pyximpor は、import 中に発生した distutils や gcc の警告やエラーを隠蔽 しません。おそらく、その方が、何かがうまくいかない場合やその理由を知り たいときに、よりよいフィードバックを得られるはずです。また、問題が無かっ た場合には、 pyximport が期待通りにモジュールをリビルドした旨、愛想を 述べるはずです。

雑談と考察

Python の reload() は、プラットフォームによっては (もしかすると、 どのプラットフォームでも?) .so の更新時に何もしないらしい。何らか の (ちょっとした) 検証が必要。ただし、 reload は Python のインタラクティ ブインタプリタ外のアプリケーションではほとんど使わないし、とりわけ C の拡張モジュールに対しては使わない。 Windows に関しては、こんな情報も ある: http://mail.python.org/pipermail/python-list/2001-July/053798.html

setup.py installsitecustomize.py を変更しません。そう したほうがよいかも?

Python の「標準インタプリタ」の挙動を変えるのは、ユーザがパッケージの インストールに期待している以上のことかもしれない...

Pyximport は .pyx と同じディレクトリに .c を出力する (.py のそばに .pyc を出力するのと同じ)。ただし、プラットフォーム固有の バイナリは、 distutils の例にならって build ディレクトリに出力する。 魔法の杖があって、 Cython や distutils などにビルドディレクトリを指定 できれば、同じディレクトリに出力するのに。トップレベルにある方が、 Cython の問題をデバッグするのに とても便利 だから。