7. 使用例

この章は distutils を使い始めるのに役立つ幾つかの基本的な例を提供します。distutils を使うための追加の情報は Distutils Cookbook で見つけることができます。

参考

Distutils Cookbook

distutils をもっと制御するためのレシピ集。

7.1. pure Python 配布物 (モジュール形式)

単に二つのモジュール、特定のパッケージに属しないモジュールを配布するだけなら、setup スクリプト中で py_modules オプションを使って個別に指定できます。

もっとも単純なケースでは、二つのファイル: setup スクリプト自体と、配布したい単一のモジュール、この例では foo.py について考えなければなりません:

<root>/
        setup.py
        foo.py

(この節の全ての図において、<root> は配布物ルートディレクトリを参照します。) この状況を扱うための最小の setup スクリプトは以下のようになります:

from distutils.core import setup
setup(name='foo',
      version='1.0',
      py_modules=['foo'],
      )

配布物の名前は name オプションで個々に指定し、配布されるモジュールの一つと配布物を同じ名前にする必要はないことに注意してください (とはいえ、この命名方法はよいならわしでしょう)。ただし、配布物名はファイル名を作成するときに使われるので、文字、数字、アンダースコア、ハイフンだけで構成しなければなりません。

py_modules はリストなので、もちろん複数のモジュールを指定できます。例えば、モジュール foobar を配布しようとしているのなら、 setup スクリプトは以下のようになります:

<root>/
        setup.py
        foo.py
        bar.py

また、セットアップスクリプトは以下のようになります

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      py_modules=['foo', 'bar'],
      )

モジュールのソースファイルは他のディレクトリに置けますが、そうしなければならないようなモジュールを沢山持っているのなら、モジュールを個別に列挙するよりもパッケージを指定した方が簡単でしょう。

7.2. pure Python 配布物 (パッケージ形式)

二つ以上のモジュールを配布する場合、とりわけ二つのパッケージに分かれている場合、おそらく個々のモジュールよりもパッケージ全体を指定する方が簡単です。たとえモジュールがパッケージ内に入っていなくても状況は同じで、その場合はルートパッケージにモジュールが入っていると Distutils に教えることができ、他のパッケージと同様にうまく処理されます (ただし、 __init__.py があってはなりません)。

最後の例で挙げた setup スクリプトは

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=[''],
      )

のようにも書けます (空文字はルートパッケージを意味します)

これら二つのファイルをサブディレクトリ下に移動しておいて、インストール先はルートパッケージのままにしておきたい、例えば:

<root>/
        setup.py
        src/      foo.py
                  bar.py

のような場合には、パッケージ名にはルートパッケージをそのまま指定しておきますが、ルートパッケージに置くソースファイルがどこにあるかを Distutils に教えなければなりません:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'': 'src'},
      packages=[''],
      )

もっと典型的なケースでは、複数のモジュールを同じパッケージ (またはサブパッケージ) に入れて配布しようと思うでしょう。例えば、 foobar モジュールがパッケージ foobar に属する場合、ソースツリーをレイアウトする一案として、以下が考えられます

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py

実際、Distutils ではこれをデフォルトのレイアウトとして想定していて、setup スクリプトを書く際にも最小限の作業しか必要ありません:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar'],
      )

モジュールを入れるディレクトリをパッケージの名前にしたくない場合、ここでも package_dir オプションを使う必要があります。例えば、パッケージ foobar のモジュールが src に入っているとします:

<root>/
        setup.py
        src/
                 __init__.py
                 foo.py
                 bar.py

適切な setup スクリプトは、以下のようになるでしょう

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': 'src'},
      packages=['foobar'],
      )

また、メインパッケージ内のモジュールを配布物ルート下に置くことがあるかもしれません:

<root>/
        setup.py
        __init__.py
        foo.py
        bar.py

この場合、setup スクリプトは

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': ''},
      packages=['foobar'],
      )

のようになるでしょう。(空文字列も現在のディレクトリを表します。)

サブパッケージがある場合、 packages で明示的に列挙しなければなりませんが、 package_dir はサブパッケージへのパスを自動的に展開します。 (別の言い方をすれば、 Distutils はソースツリーを 走査せず 、どのディレクトリが Python パッケージに相当するのかを __init__.py files. を探して調べようとします。) このようにして、デフォルトのレイアウトはサブパッケージ形式に展開されます:

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py
                 subfoo/
                           __init__.py
                           blah.py

対応する setup スクリプトは以下のようになります

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar', 'foobar.subfoo'],
      )

7.3. 単体の拡張モジュール

拡張モジュールは、ext_modules オプションを使って指定します。package_dir は、拡張モジュールのソースファイルをどこで探すかには影響しません; pure Python モジュールのソースのみに影響します。もっとも単純なケースでは、単一の C ソースファイルで書かれた単一の拡張モジュールは以下のようになります:

<root>/
        setup.py
        foo.c

foo 拡張モジュールがルートパッケージに属している場合は、 setup スクリプトは次のように書けます

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foo', ['foo.c'])],
      )

同じソースツリーレイアウトで、この拡張モジュールを foopkg の下に置き、拡張モジュールの名前を変えるには

foopkg 拡張をルートパッケージ下に所属させたい場合、 setup スクリプトは以下のようにします:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foopkg.foo', ['foo.c'])],
      )

7.4. パッケージのチェック

check コマンドで、パッケージのメタデータが配布物をビルドするための最低限の条件を満たしているかの検証が行えます。

コマンドを実行するには、単純に setup.py スクリプトを使って呼び出してください。何かが不足していた場合は、 check で警告が表示されるでしょう。

簡単なスクリプトを例に取ってみましょう:

from distutils.core import setup

setup(name='foobar')

check コマンドを走らせると、いくつかの警告が表示されます:

$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or
         (maintainer and maintainer_email) must be supplied

long_description フィールドで reStructuredText の構文を使用し、docutils がインストールされている場合は、check コマンドに restructuredtext オプションを使用して、その構文が正しいかどうかチェックできます。

例えば、 setup.py スクリプトを次のように変えたとします:

from distutils.core import setup

desc = """\
My description
==============

This is the description of the ``foobar`` package.
"""

setup(name='foobar', version='1', author='tarek',
    author_email='tarek@ziade.org',
    url='http://example.com', long_description=desc)

long description の壊れている箇所を、 docutils 構文解析器を使い check で検出できます:

$ python setup.py check --restructuredtext
running check
warning: check: Title underline too short. (line 2)
warning: check: Could not finish the parsing.

7.5. メタデータの読み込み

distutils.core.setup() 関数は、プロジェクトの setup.py スクリプトを通して、プロジェクトのメタデータフィールドの値を調べることのできるコマンドライン・インターフェイスを提供します:

$ python setup.py --name
distribute

この呼び出しは、 distutils.core.setup() 関数を実行して name メタデータを読み取っています。ソース配布物もしくはバイナリ配布物が Distutils で作られていたとしても、メタデータフィールドは PKG-INFO という静的なファイルに書かれます。Distutils ベースのプロジェクトが Python にインストールされるとき、 NAME-VERSION-pyX.X.egg-info 以下にある配布物のモジュールやパッケージと一緒に PKG-INFO がコピーされます。ここで NAME はプロジェクト名、 VERSION はメタデータで定義されたバージョン、 pyX.X2.73.2 のような Python のメジャーバージョンとマイナーバージョンです。

distutils.dist.DistributionMetadata クラスとその read_pkg_file() メソッドを使って、この静的ファイルを読むことができます:

>>> from distutils.dist import DistributionMetadata
>>> metadata = DistributionMetadata()
>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
>>> metadata.name
'distribute'
>>> metadata.version
'0.6.8'
>>> metadata.description
'Easily download, build, install, upgrade, and uninstall Python packages'

Notice that the class can also be instantiated with a metadata file path to loads its values:

>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
>>> DistributionMetadata(pkg_info_path).name
'distribute'