2009年12月7日

repoze.bfgで遊びながら、pasteの復習しつつ、buildoutの使い方に慣れる(1)

id:ytakeuchさんからZope/Ploneアドベントカレンダーのバトンが回ってきました。
repozeは、zope由来のコンポーネントをWSGIアプリケーションで利用できるようにしたコンポーネント集です。
それらのコンポーネントを再構成したrepoze.bfgというフレームワークが提供されています。
repoze.bfgは、ZODBやURLトラバーサルなど、Zopeのベースとなるアーキテクチャを利用しつつ、必要なコンポーネントはその都度取り込むことができます。

環境構築
distribute, virtualenv, buildoutを利用した環境構築をします。
この辺りの話は@shimizukawa先生のこちらのエントリが詳しいです。
まずは、virtualenvで仮想python環境を作ります。今回は、--distributeオプションを使ってsetuptoolsの代わりにdistributeを使います。
virtualenv --no-site-packages --distribute env
できあがった仮想環境に入る。
. env/bin/activate
作業用ディレクトリを作ります。
mkdir board
cd board
buildout環境を作ります。bootstrap.pyはこことかここにありますので、適当に。
bootstrap.pyにも--distributeオプションを付けます。
cp /path/to/bootstrap.py .
python bootstrap.py --ditribute

buildout.cfgを作成。
PasteScriptでソースベースを生成するために、repoze.bfgとPasteScriptを入れます。
buildout.cfg
[buildout]
parts = repoze
[repoze]
recipe = zc.recipe.egg
eggs =
 PasteScript
 repoze.bfg

ローカルのbuildoutを実行して環境を更新
bin/buildout
ローカルにpasterコマンドが入ります。また、プロジェクトテンプレートにrepoze.bfgのものが追加されるので、これらを使ってプロジェクトを作成します。
bin/paster create -t bfg_zodb board
mv board src

次にテスト用にnoseを導入します。
また、テスト対象のプロジェクトもeggで追加しないと参照できないので、作成したboardプロジェクトのsrcディレクトリをdevelopに追加してeggとして取り扱えるようにします。

buildout.cfg
[buildout]
parts =
 repoze
 webtest
develop = src
[repoze]
recipe = zc.recipe.egg
eggs =
 PasteScript
 repoze.bfg

[webtest]
recipe = zc.recipe.egg:scripts
eggs =
 distribute
 board
 nose
 WebTest
initialization =
 sys.argv[1:1] = ['-w', 'board', '--with-doctest']
scripts =
 nosetests=webtest
上の設定では、WebTestもテストで利用できるようにeggsに追加しています。
また、コマンドラインを編集して、srcディレクトリ以下でdoctestを有効にしたテストを実行するようにして、コマンド名をwebtestにしています。
試しにテスト実行してみます。
bin/webtest

生成されたプロジェクトを探索する
pasterで生成されたプロジェクトの中身を確認してみます。
PasteScriptでは、PasteDeployによる設定ファイルを読み込んでWSGIアプリケーションを実行します。
repoze_zodbテンプレートで生成されたプロジェクトでは、[パッケージ名].iniが設定ファイルになっています。
pipeline:main がwsgiアプリの起点になります。
pipelineでは、WSGIミドルウェアの設定が書かれており、さらにapp:zodbで設定されているWSGIアプリを参照します。
app:zodbでは、use = egg:board#appと書かれている部分が実際のWSGIアプリです。その他は、WSGIアプリを呼び出すときに渡される設定値です。
egg:board#appってなんでしょう?
まずは、eggパッケージです。
eggには、entry_pointsという仕組みがあり、eggパッケージが提供するインターフェイスのようなものを公開しています。
boardのegg情報は、src/board.egg_info以下にあり、entry_points.txtにentry_pointsの情報が記述されています。
      [paste.app_factory]
      app = board.run:app
board.runモジュールのappオブジェクトがpaste.app_factoryインターフェイスとして公開されています。
これが、board.iniに書かれているegg:blog#appの正体です。

paste.app_factoryは、設定を受け取り、wsgiアプリケーションを返す関数が求められます。

では、src/board/run.pyを見てみましょう。
appは関数で定義されています。

def app(global_config, **settings):

global_configはboard.iniのDEFAULTセクションで設定されている内容です。
settingsは、board.iniのapp:zodbセクションで設定されている内容です。
appはglobal_configやsettingsの内容を使って、WSGIアプリケーションを作成します。

動かしてみる
pasterコマンドで、wsgiアプリを実行します。
bin/paster serve src/blog.ini
このときのhttpサーバーは、board.iniでserver:mainセクションに設定されています。






追記


次のバトンはnyusukeにお願いしました。