buildoutで開発2: buildoutで環境を整える¶
eggの作り方が分からない, buildoutで開発1: WSGIアプリをeggで作る の続き。buildoutでテストまで。
現時点のファイル構成:
c:\Project\buildout\env1\
+-- wsgiapp
+-- setup.cfg
+-- setup.py
+-- wsgi.ini
+-- wsgiapp.egg-info
| +-- *.*
+-- wsgiapp
+-- __init__.py
+-- __init__.pyc
+-- startup.py
+-- startup.pyc
とりあえずコミットしておく。 *.pycと*.egg-info は自動生成されるファイルなのでコミットしない。 Subversion上に以下のようになるようにコミットして、trunkをチェックアウト。
リポジトリの内容はこんな感じ:
svn://
+-- wsgiapp
+-- trunk
+-- setup.cfg
+-- setup.py
+-- wsgi.ini
+-- wsgiapp
+-- __init__.py
+-- startup.py
以降、ソースコード管理に登録する手順を何度か書いてますが、以降の作業では、登録するべきファイルとそうでないファイルが混在し始めるため、必要なファイルを分かりやすくするためにコミットしてます。
閑話休題。buildoutを使うための環境を整える。
buildout.cfg:
[buildout]
develop = .
parts = wsgiapp
newest = true
[wsgiapp]
recipe = zc.recipe.egg
eggs =
wsgiapp
Paste
PasteDeploy
PasteScript
ここまでで一度コミット。
おもむろに違う環境を作ってみる。別のコンソールを開くと良いと思う。
この環境ではvirtualenv と setuptoolsが使える以外は何も入っていない。 bootstrap.py -> buildout -> setuptools... と関連して色々ダウンロードされて、環境が構築される。
ここで、元の環境にもどって、wsgiappに関連パッケージを定義する。 パッケージの関連づけはbuildoutとしてではなく、eggとして対応する。 このため setup.py を以下のように更新する。
setup.py:
install_requires=[
'BeautifulSoup',
],
ここで、wsgiapp.egg-info/requires.txt を見ると、ちゃんとBeautifulSoupに依存しているという定義にUpdateされている。
これからBeautifulSoupを使うような実装を追加したいが、eggで追加されたパッケージの動作を確認したり、ヘルプを見たりするのにインタラクティブシェルからBeautifulSoupを呼び出したい。 でも、buildoutで関連づけられたeggパッケージはPythonにインストールされているわけではないので、そのままでは呼び出せない。
そこで、関連するeggを使える状態でPythonを起動するスクリプトを作成する。スクリプトの名前は適当にpyとしておきます。 スクリプトの用意は、以下のようにbuildout.cfgを書き換えれば、 あとはbuildoutがやってくれる。
buildout.cfg:
[buildout]
develop = .
parts = wsgiapp eggpy
newest = true
[wsgiapp]
recipe = zc.recipe.egg
eggs =
wsgiapp
Paste
PasteDeploy
PasteScript
[eggpy]
recipe = zc.recipe.egg
eggs = ${wsgiapp:eggs}
interpreter = py
scripts = py
[eggpy]
セクションを追加して、そのセクションがbuild対象であることをbuildoutに伝えるために、 parts =
にeggpyを追加。
eggpyの中で、利用したいeggの指定はwsgiappと同じ内容で良いけど、それをまた書くのは面倒なので、 ${wsgiapp:eggs}
という感じで変数で指定。
この内容で環境を更新するために、buildoutコマンドを実行。
作られたpyコマンドでインタラクティブシェルを起動して、eggパッケージを呼び出せることを確認。
OK.
BeautifulSoupを使ったWSGIアプリの実装部分関数を作る。とりあえずWSGIとか関係なく、与えられたURLをGETして、hrefの値を書き換えて返す関数を実装。動作確認用に、コンソールから実行された場合の動作も実装しておく。
wsgiapp/scraper.py:
# -*- coding: utf-8 -*-
import urllib2
from BeautifulSoup import BeautifulSoup
def modifyLinks(url):
bs = BeautifulSoup(urllib2.urlopen(url))
for elem in bs.findAll('a'):
if elem.has_key('href'):
elem['href'] += "#foobar"
return bs.prettify()
if __name__ == '__main__':
import sys
if len(sys.argv) > 1:
url = sys.argv[1]
else:
url = "http://pypi.python.org/simple/BeautifulSoup/"
print modifyLinks(url)
で、動作確認。
OK. ちゃんと#foobarが追加されてた。 これをwsgiappとして組み込む。
wsgiapp/startup.py:
# -*- coding: utf-8 -*-
import scraper
def application(environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/html')]
start_response(status, response_headers)
return [scraper.modifyLinks(
"http://pypi.python.org/simple/BeautifulSoup/"
)]
def application_factory(global_conf):
return application
うまく動くか、pasterコマンドでrequestして確認したり、paster serve してブラウザで確認したり。
ここまでをとりあえず、コミット。
ここで、さっき作ったscraperのテスト方法が気に入らないので、書き換えてみる。
wsgiapp/scraper.py:
# -*- coding: utf-8 -*-
import urllib2
from BeautifulSoup import BeautifulSoup
def modifyLinks(url):
"""modifyLinks get content from given url and modify href attributes.
>>> content = modifyLinks("http://pypi.python.org/simple/BeautifulSoup/")
>>> '#foobar"' in content
True
"""
bs = BeautifulSoup(urllib2.urlopen(url))
for elem in bs.findAll('a'):
if elem.has_key('href'):
elem['href'] += "#foobar"
return bs.prettify()
if __name__ == '__main__':
import doctest
doctest.testmod()
で、改めてテスト。エラー無くテストが成功した場合は、 -v
オプション無しだと何も表示されないので、心配なら-vを付けて動かしてみよう。
テストが通ったので、コミット。
最後に、buildoutで全モジュールを自動的にテストするためのスクリプトを用意する。まず、DocTestを外から呼び出すためにtests.pyを用意。
wsgiapp/tests.py:
# -*- coding: utf-8 -*-
import unittest
from doctest import DocTestSuite
def test_suite():
return unittest.TestSuite((
DocTestSuite('wsgiapp.scraper'),
))
if __name__ == '__main__':
unittest.main()
次に、biuldout.cfgでテスト実行スクリプトを生成。
[test]
セクションを追加して、partsにtestセクションの呼び出しを追加。使っているレシピが今までと違ってzc.recipe.testrunnerであることと、テスト対象にPaste等を含めたくなかったので、${wsgiapp:eggs}は使わなかったところがポイント。
buildout.cfg:
...
parts = wsgiapp eggpy test
...
[test]
recipe = zc.recipe.testrunner
eggs = wsgiapp
relative-paths = true
buildoutで環境を更新してテストする。
ZopeのTestRunnerが使われるけど、気にしない方向で。 bin/test -h
でコマンドラインオプションもみれるよ。
今日はここまで。