# 6. distutils の例

注釈

このドキュメントは、 https://setuptools.readthedocs.io/en/latest/setuptools.html にある `setuptools` のドキュメントが現時点でここにある関連情報を全て網羅するまで、単独でここに載せておかれます。

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

参考

Distutils Cookbook

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

## 6.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` はリストなので、もちろん複数のモジュールを指定できます。例えば、モジュール `foo` と `bar` を配布しようとしているのなら、 setup スクリプトは以下のようになります:

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

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

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

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

## 6.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=[''],
          )

もっと典型的なケースでは、複数のモジュールを同じパッケージ (またはサブパッケージ) に入れて配布しようと思うでしょう。例えば、 `foo` と `bar` モジュールがパッケージ `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'],
          )

## 6.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'])],
          )

## 6.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.

## 6.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.X` は `2.7` や `3.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'

このクラスは、値を読み取るメタデータのファイルパスを引数に取ってインスタンス化することもできることに注意してください:

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