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) should 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'
