Date: 2009-07-15
Tags: python, web

buildoutで開発1: WSGIアプリをeggで作る

eggの作り方が分からない のエントリで書いたように、egg対応アプリの作成手順についてまとまった情報が見つからなかったので、自分でやってみた手順をメモしていきます。 egg recipeの作り方 で検索しても、ハムエッグの調理法ばっかりひっかかる。

まずはegg対応のパッケージを作ってみる。せっかくなので、中身を超簡単なWSGIアプリにしてPasterで起動できるようにしてみる。 下準備として、virtualenvで独立した環境を用意。virtualenvはeasy_installでインストールしてある前提。

これでpasterコマンドでスケルトンを作ったり色々できるようになったので、どんなテンプレートがあるのかを眺めつつ、スケルトンを作成。

項目の内容は適当に入れたけど、将来的にpypiに登録するつもりならちゃんと入れたいところ。 コマンド一発でpypiに登録できるらしいけど、上記のような適当な内容で登録しちゃわないように気をつけること>俺。

次に、pasterでWeb起動できるようにエントリポイントを登録する。使えるエントリポイント一覧は paster points --list で表示出来るっぽい。

wsgi.ini, startup.py を作成して、setup.pyにエントリポイントとして登録する。この3つの更新と各種の名前(モジュール名・関数名等)が関連しあっているようだ。エントリポイントについてはまだちゃんと理解しきれていないのであとでもうちょっと調べる。

wsgi.ini:

[app:main]
use = egg:wsgiapp

wsgiapp/startup.py:

# -*- coding: utf-8 -*-

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ["It's Python!\\n"]

def application_factory(global_conf):
    return application

setup.py (変更点のみ):

entry_points="""
[paste.app_factory]
main = wsgiapp.startup:application_factory
""",

setup.py を変更したので、eggとしての情報を更新するために python setup.py develop する。これで *.egg-info が更新される。 ところで、この *.egg-info はソースコード管理に入れない方が良いんだと思うけど、どうなんだろうか?

これでpasterから実行出来るようになった。 作ったアプリにrequestを投げてみる。

It's Work!

アプリに渡ってきている環境変数とかを表示するように、改造してみる。

wsgiapp/startup.py:

# -*- coding: utf-8 -*-
from StringIO import StringIO
from pprint import pprint

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    buf = StringIO()
    pprint(environ, buf)
    return ["It's Python!\\n" + buf.getvalue()]

def application_factory(global_conf):
    return application

今回はsetup.pyを(egg的な情報を)変更していないので、setup.py develop はしなくてもOK。 さっそくRequestにQueryを付けて投げてみる。

ちゃんと受け取れているっぽい。

ちょっといじれば、Webサーバーとして起動して、ブラウザでアクセスすることも出来るよ!

wsgi.ini:

[app:main]
use = egg:wsgiapp

[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 8080

これで、ブラウザで http://localhost:8080/hoge?foo=bar&baz=2 にアクセスすると以下のように表示される:

It's Python!
{'CONTENT_LENGTH': '0',
 'CONTENT_TYPE': '',
 'HTTP_ACCEPT': 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
 'HTTP_ACCEPT_CHARSET': 'Shift_JIS,utf-8;q=0.7,*;q=0.3',
 'HTTP_ACCEPT_ENCODING': 'gzip,deflate,bzip2,sdch',
 'HTTP_ACCEPT_LANGUAGE': 'ja,en-US;q=0.8,en;q=0.6',
 'HTTP_CONNECTION': 'keep-alive',
 'HTTP_HOST': 'localhost:8080',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.33 Safari/530.5',
 'PATH_INFO': '/hoge'
 'QUERY_STRING': 'foo=bar&baz=2',,
 'REMOTE_ADDR': '127.0.0.1',
 'REQUEST_METHOD': 'GET',
 'SCRIPT_NAME': '',
 'SERVER_NAME': '127.0.0.1',
 'SERVER_PORT': '8080',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'paste.httpserver.thread_pool': <paste.httpserver.ThreadPool object at 0x01889F90>,
 'wsgi.errors': <open file '<stderr>', mode 'w' at 0x012EE0B0>,
 'wsgi.input': <socket._fileobject object at 0x019E80A0 length=0>,
 'wsgi.multiprocess': False,
 'wsgi.multithread': True,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'http',
 'wsgi.version': (1, 0)}

最後にApacheにmod_wsgiを設定して表示する。 mod_wsgiはGoogleCode から取得。自分の環境はWindowsなので自前でビルドしました。

httpd-wsgi.conf:

LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonPath c:/project/buildout/env1/lib
WSGIPythonEggs c:/project/buildout/env1/wsgiapp
WSGIScriptAlias /test c:/project/buildout/env1/wsgiapp/wsgiapp/startup.py

<Directory c:/project/buildout/env1/wsgiapp/wsgiapp/>
    Order allow,deny
    Allow from all
</Directory>

これでとりあえず http://localhost/test にブラウザでアクセスすると表示出来た! けど、mod_wsgiとの繋ぎ込み部分(startup.py直接指定)が納得いかない。納得いかないけど、とりあえず放置。

最後に、egg化する。

ということで、wsgiapp-0.1dev-py2.4.egg が作れました。今日はここまで。

次はこのeggを使ってApacheと繋げられるようになれば良いのかな。