新たなツールに慣れる最も簡単な方法は実際に実行できるサンプルを見てみることです。つまり、どのように Paver を使い始めれば良いかということになります。Paver ディストリビューション内の docs/sample にサンプルがあります。そして、その “started” ディレクトリにスタートガイドのサンプルがあります。
最初のサンプルは “旧い方法” と呼びます(docs/samples/started/oldway ディレクトリにあります)。それは配布できるようにしたい Python パッケージとドキュメントの適正且つ典型的なプロジェクトです。
Python の distutils を使用すると配布パッケージをとても簡単に作成できます。次のような setup.py ファイルを作成します。
#<== include('started/oldway/setup.py')==>
from distutils.core import setup
setup(
name="TheOldWay",
packages=['oldway'],
version="1.0",
url="http://www.blueskyonmars.com/",
author="Kevin Dangoor",
author_email="dangoor@gmail.com"
)
#<==end==>
簡単な setup スクリプトで実行します。
python setup.py sdist
ソースディストリビューションをビルドするには、
# <==
# sh('cd docs/samples/started/oldway; python setup.py sdist',
# insert_output=False)
# sh('ls -l docs/samples/started/oldway/dist')
# ==>
celkem 4
-rw-r--r-- 1 almad almad 629 11. lis 22.19 TheOldWay-1.0.tar.gz
# <==end==>
それからユーザは見慣れたコマンドを実行します。
python setup.py install
パッケージをインストールするために setuptools を使用するとさらに簡単です。
easy_install "TheOldWay"
パッケージは Python パッケージインデックスにあります。
旧い方法プロジェクトはドキュメントに Sphinx を使用しているので少なくともちょっとはモダンです。sphinx-quickstart を使用してドキュメントを作成し始めるとき Sphinx は HTML ドキュメントを生成する Makefile を設けるでしょう。そのため、HTML ドキュメントを簡単に生成できます。
make html
但し、(Paver そのもののように)このプロジェクトでは、ユーザへヘルプを定期ょ湯するためにパッケージの docs ディレクトリに HTML ファイルを追加したいです。最終的に次のようなシェルスクリプトを作ります。
# <== include("started/oldway/builddocs.sh")==>
cd docs
make html
cd ..
rm -rf oldway/docs
mv docs/_build/html oldway/docs
# <==end==>
もちろん、このようなスクリプトを作成することは、実際にそのスクリプトの実行を覚えておく必要があることを意味します。このスクリプトを “buildsdist.sh” に変更して、ファイルの最後に python setup.py sdist を追加することはできます。しかし、直接 python setup.py sdist を実行できればもっと良い気がしますよね?
新たな distutils コマンドを作成する こともできますが、Python ライブラリディレクトリの distutils/command パッケージにそんな機能を本当に追加したいでしょうか?そして、いずれにしても sdist コマンドをどうやって呼び出すのでしょうか? setuptools は助けてくれますが、それでもこのコマンドのコレクションのためにエントリポイントとモジュールのセットアップが必要になります。
これを読んで “うわっ、なんて不自然なサンプルだ!” と考える人がいることも 分かります 。プロジェクトのビルド、パッケージング、配布、デプロイは全てのプロジェクトでかなりカスタマイズされます。Paver の重要なことの1つはプロジェクトでどんな奇妙な要求が起こっても簡単に扱えるようにすることです。このサンプルは不自然に見えるかもしれませんが、どうやって Paver がそういったタスクを簡単に扱うかについてのアイディアを説明します。
これまでのセクションのスクリプティングを今すぐクリーンアップして Paver を少し取り入れてみましょう。Paver を使用するために既存のプロジェクトから移行することは、本当に、本当に簡単です。旧い方法の setup.py から setup() 関数を呼び出してください。
# <== include("started/oldway/setup.py", "setup")==>
setup(
name="TheOldWay",
packages=['oldway'],
version="1.0",
url="http://www.blueskyonmars.com/",
author="Kevin Dangoor",
author_email="dangoor@gmail.com"
)
# <==end==>
setup.py は普通の Python スクリプトです。それは慣例として setup.py と呼ばれます。Paver は Make や Rake のように動作します。Paver を使用するために paver <taskname> を実行します。そして paver コマンドはカレントディレクトリの pavement.py ファイルを探します。pavement.py は普通の Python モジュールです。典型的な pavement.py は paver.easy から便利な関数、オブジェクトのセットをインポートします。それから役に立つタスクを含むその他のモジュールをインポートします。
# <== include('started/newway/pavement.py', 'imports')==>
from paver.easy import *
import paver.doctools
from paver.setuputils import setup
# <==end==>
setup.py から pavement.py に変換することは簡単です。Paver は全てのビルドオプションを含む特殊な options オブジェクトを提供します。 options は属性スタイルのアクセスを許可して特別な検索機能を持つ辞書です。distutils の操作オプションはそのオプションの setup セクションに格納されます。そして、慣例として Paver はその options セクションに値をセットする setup 関数を提供します(さらに一歩踏み込むと、全ての distutils/setuptools コマンドを作ることで Paver のタスクとして利用できます)。ここにその移行がどのように行うかを紹介します。
# <== include('started/newway/pavement.py', 'setup')==>
setup(
name="TheNewWay",
packages=['newway'],
version="1.0",
url="http://www.blueskyonmars.com/",
author="Kevin Dangoor",
author_email="dangoor@gmail.com"
)
# <==end==>
Paver を選択するということは distutils や setuptools を使用しないということではありません。Paver は distutils や setuptools コマンドを使用し続けます。Paver のタスクを持つモジュールをインポートするとき、それらのタスクは実行時に自動的に利用可能になります。distutils や setuptools に対して同様にアクセスしたいなら、上述したように paver.setuputils.setup を使用するか paver.setuputils.install_distutils_tasks() を呼び出すか、どちらでも使用することができます。
実際に paver help を実行すると見ることができます。
# <== sh('cd docs/samples/started/newway; paver help')==>
Could not load entry point: upload_docs = setuptools.command.upload_docs:upload_docs
---> paver.tasks.help
Usage: paver [global options] taskname [task options] [taskname [taskoptions]]
Options:
--version show program's version number and exit
-n, --dry-run don't actually do anything
-v, --verbose display all logging output
-q, --quiet display only errors
-i, --interactive enable prompting
-f FILE, --file=FILE read tasks from FILE [pavement.py]
-h, --help display this help information
Tasks from distutils.command:
bdist - create a built (binary) distribution
bdist_dumb - create a "dumb" built distribution
build - build everything needed to install
build_clib - build C/C++ libraries used by Python extensions
build_scripts - "build" scripts (copy and fixup #! line)
clean - clean up temporary files from 'build' command
install_data - install data files
install_headers - install C/C++ header files
upload - upload binary package to PyPI
Tasks from nose.commands:
nosetests - Run unit tests using nosetests
Tasks from paver.doctools:
cog - Runs the cog code generator against the files matching your
specification
doc_clean - Clean (delete) the built docs
html - Build HTML documentation using Sphinx
uncog - Remove the Cog generated code from files
Tasks from paver.misctasks:
generate_setup - Generates a setup
minilib - Create a Paver mini library that contains enough for a simple
pavement
Tasks from paver.tasks:
help - This help display
Tasks from setuptools.command:
alias - define a shortcut to invoke one or more commands
bdist_egg - create an "egg" distribution
bdist_rpm - create an RPM distribution
bdist_wininst - create an executable installer for MS Windows
build_ext - build C/C++ and Pyrex extensions (compile/link to build directory)
build_py - "build" pure Python modules (copy to build directory)
develop - install package in 'development mode'
easy_install - Find/get/install Python packages
egg_info - create a distribution's .egg-info directory
install - install everything from build directory
install_egg_info - Install an .egg-info directory for the package
install_lib - install all Python modules (extensions and pure Python)
install_scripts - install scripts (Python or otherwise)
register - register the distribution with the Python package index
rotate - delete older distributions, keeping N newest files
saveopts - save supplied options to setup.cfg or other config file
sdist - create a source distribution (tarball, zip file, etc.)
setopt - set an option in setup.cfg or another config file
test - run unit tests after in-place build
Tasks from sphinx.setup_command:
build_sphinx - Build Sphinx documentation
Tasks from pavement:
deploy - Deploy the HTML to the server
html - Build the docs and put them into our package
sdist - Overrides sdist to make sure that our setup
# <==end==>
ヘルプコマンドは利用可能な全てのタスクを表示します。その上部付近に distutils.command からのタスクがあります。標準の distutils の全コマンドが利用できます。
Python パッケージが適切に再配布可能になる前に行う必要があることが1つあります。distutils に特別なファイルを伝えます。それは簡単な MANIFEST.in で実行できます。
# <== include('started/newway/MANIFEST.in')==>
include setup.py
include pavement.py
include paver-minilib.zip
# <==end==>
そのファイルを使用して paver sdist を実行すると、最終的に等価な出力ファイルが生成されます。
# <==
# sh('cd docs/samples/started/newway; paver sdist',
# insert_output=False)
# sh('ls -l docs/samples/started/newway/dist')
# ==>
celkem 32
-rw-r--r-- 1 almad almad 28860 11. lis 22.19 TheNewWay-1.0.tar.gz
# <==end==>
新しい方法プロジェクトのユーザはシステム上にパッケージをインストールするために paver install も実行できることにもなります。
python setup.py install は長い間使われてきました。 paver install を実行することをユーザへ伝えるためにパッケージ内に必ず README を置くようにしますが、誰も実際にはそのドキュメントを読まないことを私たちみんなが知っています。(そうそう、このガイドを読む時間を取ってくれてありがとう!)
そうだとしても心配しないで。tarball の中に入れる setup.py ファイルを生成するために paver generate_setup を実行することができます。それから、ユーザは慣れた方法で python setup.py install を実行して Paver がその処理を引き継ぎます。
まだ Paver を持っていない Python のインストール方法は何百万もありますが Python と distutils は持っています。どうやって Pathon ベースのインストールが実行されるだろうか?
それは簡単です、 paver minilib を単に実行すると paver-minilib.zip というファイルが作られます。そのファイルには多くのプロジェクトをインストールするために Paver の十分な機能を持っています。Paver が生成した setup.py はそのファイルを探すようになっていて、見つかったらそのファイルを使用します。
あなたのパッケージの肥大化が心配? paver-minilib は大きくありません。
# <==
# sh('cd docs/samples/started/newway ; paver minilib',
# insert_output=False)
# sh('ls -l docs/samples/started/newway/paver-minilib.zip')
# ==>
-rw-r--r-- 1 almad almad 27309 11. lis 22.19 docs/samples/started/newway/paver-minilib.zip
# <==end==>
Paver そのものは生成された setup ファイルと paver-minilib でブートストラップされます。
新しい方法プロジェクトの適正なディストリビューションを作るために3つのコマンドが実行できることに気付いたかもしれません。えっと、実際に全てのコマンド paver generate_setup minilib sdist を同時に実行することはできます。しかし、それは思うほどひどい方法ではありませんが、良い方法でもありません。実際にはあなたはそのタスクのどれかを忘れるので、そのことにより最終的に壊れたディストリビューションを作りたくはないと思います。
意図的に、Paver で実行する最も簡単な方法の1つは既存の “タスク” の処理を拡張して distutils コマンドを含めるようにします。必要な全ての処理を pavement.py の新たな sdist タスクとして作成することです。
# <== include('started/newway/pavement.py', 'sdist')==>
@task
@needs('generate_setup', 'minilib', 'setuptools.command.sdist')
def sdist():
"""Overrides sdist to make sure that our setup.py is generated."""
pass
# <==end==>
@task デコレータは Paver へこれはタスクであって関数ではないことを伝えます。@needs デコレータはこのタスクが実行される前に実行すべき他のタスクを指定します。お好みでタスク内で call_task(taskname) 関数を呼び出すこともできます。その関数名はタスクの名前を決定します。docstring は Paver のヘルプに出力される内容になります。
上述した pavement.py のタスクで setup ファイルと minilib を生成した後でソースディストリビューションをビルドするために必要なコマンドは paver sdist だけです。
そのツールそのものが pavement を簡単に作成するタスクと関数を提供するまで Paver の標準ライブラリは共通ツールとして役立つモジュールのコレクションを追加します。Sphinx は Paver がビルトインサポートしているパッケージの1つです。
Paver の Sphinx サポートを使用するために Sphinx をインストールする必要があります。そして pavement.py に import paver.doctools を追加します。インポートを実行するだけで doctools 関連のタスクが利用できます。 paver help html を実行すると html コマンドの使用方法を表示します。
# <== sh('paver help paver.doctools.html')==>
Could not load entry point: upload_docs = setuptools.command.upload_docs:upload_docs
---> paver.tasks.help
paver.doctools.html
-------------------
Usage: paver paver.doctools.html [options]
Options:
-h, --help display this help information
Build HTML documentation using Sphinx. This uses the following
options in a "sphinx" section of the options.
docroot
the root under which Sphinx will be working. Default: docs
builddir
directory under the docroot where the resulting files are put.
default: build
sourcedir
directory under the docroot for the source files
default: (empty string)
# <==end==>
表示されたヘルプによると “_build” という builddir を使用するので builddir を設定する必要があります。pavement.py にこの設定を追加してみましょう。
# <== include('started/newway/pavement.py', 'sphinx')==>
options(
sphinx=Bunch(
builddir="_build"
)
)
# <==end==>
これにより paver html は Sphinx が持っていた Makefile を使用して make html を実行することと等価になります。
生成したドキュメントを適切な場所へ移動するシェルスクリプトを覚えておいた方が良いです。
# <== include('started/oldway/builddocs.sh')==>
cd docs
make html
cd ..
rm -rf oldway/docs
mv docs/_build/html oldway/docs
# <==end==>
理想的には、ドキュメントを生成するときはこのように実行したいです。タスクをオーバーライドする方法は既に確認したので、ここでそれをやってみましょう。
# <== include('started/newway/pavement.py', 'html')==>
@task
@needs('paver.doctools.html')
def html(options):
"""Build the docs and put them into our package."""
destdir = path('newway/docs')
destdir.rmtree()
builtdocs = path("docs") / options.builddir / "html"
builtdocs.move(destdir)
# <==end==>
この設定には少し興味深いことがあります。オーバーライドしたタスクを使用するので ‘make html’ と @needs(‘paver.doctools.html’) は等価になります。
タスク内部では “path” を使用しています。これは Jason Orendorff がカスタマイズした path モジュールです。どのようなファイルやディレクトリ操作もこのモジュールを使用すると超簡単になります。
新たに生成したファイルを対象ディレクトリにコピーするので、先ずはそのディレクトリを削除することから始めます。次に移動するためのビルドした docs ディレクトリを探します。
# <== include('started/newway/pavement.py', 'html.builtdocs')==>
builtdocs = path("docs") / options.builddir / "html"
# <==end==>
path オブジェクトの優れた機能の1つはパスを構成するために ‘/’ 演算子を直感的且つ快適に使用できることです。
次にここで紹介したオプションのアクセスです。オプションオブジェクトはタスクで利用できます。それは基本的に属性スタイルのアクセスを提供するディレクトリで(長い options.sphinx.builddir の代わりに options.builddir を入力できる)オプションの変数を検索できます。オプションのプロパティはセクション間でプロパティを共有可能にするためにも便利です。
これにより、分割したファイルとしてシェルスクリプトを取り除きます。
旧い方法のドキュメントでは、実際にその関数の内部に直接的に docs を追加しました。しかし、その場所でカット&ペーストする必要がありました。Sphinx はドキュメントに外部ファイルを含める方法を提供します。Paver はさらにもっと良い方法で追加します。
ドキュメント作成には複数の問題があります。
#1 と #3 は相互排他的ですが、そうではありません。Paver ではこの問題を解決するために2種類の戦略があります。1つ目の戦略を理解するために index.rst の一部を見てましょう。
# <== include("started/newway/docs/index.rst", "mainpart")==>
Welcome to The New Way's documentation!
=======================================
This is the Paver way of doing things. The key functionality here
is in this powerful piece of code, which I will `include` here in its entirety
so that you can bask in its power::
# [[[cog include("newway/thecode.py", "code")]]]
# [[[end]]]
# <==end==>
新しい方法の index.rst では、このスタートガイドで使用されているのと同じ仕組みが見れます。Paver は Ned Batchelder の Cog パッケージを含みます。Cog はあるファイルに Python のスニペットを用意して、それらのスニペットが生成した出力内容をそのファイルの中に組み入れます。テンプレート言語とは違い、Cog はファイルにマーカーを入れて必要になる度に再度生成できるように設計されています。テンプレート言語だと、テンプレートと最終的な出力結果を持っていますが、1つのファイルに両方の内容は持てません。
そのため、私がこのスタートガイドを書いているように、チラッと見て index.rst のコンテンツの適切なインラインを確認できます。そこにある #[[[cog の部分は include() 関数を呼び出していることに気付きます。これは Paver で提供される2つめの戦略です。Paver は Cog で使用する “includedir” を指定させます。これは関連するファイルをそのディレクトリに含めるようにさせます。そして、決定的に追加したい部分のみを簡単に含めることができるように、そういったファイルのセクションを区切らせます。上述したサンプルでは newway/thecode.py ファイルの ‘code’ セクションから取り出します。それでは newway/thecode.py ファイルの中身を覗いてみましょう。
# <== sh("cat docs/samples/started/newway/newway/thecode.py") ==>
"""This is our powerful, code-filled, new-fangled module."""
# [[[section code]]]
def powerful_function_and_new_too():
"""This is powerful stuff, and it's new."""
return 2*1
# [[[endsection]]]
# <==end==>
Paver は名前セクションを定義するために Cog のような構文があります。そのため、関連するファイ名と追加したいそのセクションで include() 関数を使用してください。セクションはネストして使用することもできます(ドット表記でネストされたセクションを参照します)。
pavement.py は普通の Python プログラムです。ループやその他の構文はあなたが慣れ親しんだコーディングと同じです。そのオプションは普通の Python プログラムなのでリストやその他のオブジェクトを含めることができます。複数のホストへデプロイする必要があるときは?単にそのオプションにホストを追加してループさせるだけで良いです。
複数のサーバへ新しい方法プロジェクトの HTML ファイルをデプロイしたいと仮定してください。私は1つのサーバしか持っていないけれど、これは私が Paver そのものに行うこととよく似ています。先ず、deploy タスクに使用する複数の変数を設定します。
# <== include('started/newway/pavement.py', 'deployoptions')==>
options(
deploy = Bunch(
htmldir = path('newway/docs'),
hosts = ['host1.hostymost.com', 'host2.hostymost.com'],
hostpath = 'sites/newway'
)
)
# <==end==>
ご覧の通り、オプションの中にどのようなオブジェクトでも望みのモノを追加できます。今は deploy タスクそのもののためです。
# <== include("started/newway/pavement.py", "deploy")==>
@task
@cmdopts([
('username=', 'u', 'Username to use when logging in to the servers')
])
def deploy(options):
"""Deploy the HTML to the server."""
for host in options.hosts:
sh("rsync -avz -e ssh %s/ %s@%s:%s/" % (options.htmldir,
options.username, host, options.hostpath))
# <==end==>
新たな “cmdopts” デコレータがあります。pavement に記述したくないパスワードのような機密情報があると仮定してください。cmdopts を使用するそのタスクのコマンドラインオプションを簡単に作ることができます。options.deploy.username はコマンドラインでユーザが入力した内容をセットします。
オプションオブジェクト内を探す必要もありません。Paver はそのタスクとして同じ名前でセクションにあるオプションを優先します。そのため、options.username は他のセクションで username が存在したとしても options.deploy.username を優先して選択します。
deploy タスクは各ホストに rsync コマンドを実行するために単純なループを使用します。rsync コマンドがどうなるかを確認するためにユーザ名を入力して dry run を実行してみましょう。
# <== sh("cd docs/samples/started/newway; paver -n deploy -u kevin")==>
---> pavement.deploy
rsync -avz -e ssh newway/docs/ kevin@host1.hostymost.com:sites/newway/
rsync -avz -e ssh newway/docs/ kevin@host2.hostymost.com:sites/newway/
# <==end==>
最初に行うことは Paver を使い始めることです。これまで説明したように、あなたのワークフローに Paver を組み込むことは既存プロジェクトであっても簡単です。
paver help コマンドを使用してください。
あなたが今すぐ詳細を知りたいなら pavement ファイル と Paver 標準ライブラリ を読んでみると良いでしょう。