PythonにおけるWebフレームワーク
数年前にPythonでWebアプリケーションを書こうとした人たちは、選択肢の多さに戸惑ったことでしょう。一群のWebフレームワークの中からどれかを選び、アプリケーションをプロダクション環境にデプロイする妥当な方法を考えなければならなかったはずです。そういうわけで、Pythonは千個のフレームワークを持つ言語だというジョークが流布することになったのです。
Pythonコミュニティには、この問題の解決策として、フレームワークの数を絞るか、それとも多様性を良しとするかという選択がありました。コミュニティの性格上、フレームワークを絞り込むのは魅力的なオプションに見えなかったので、Webの開発にPythonを使用するための障壁を低くする方法としてPEP 333が書かれ、Web Server Gateway Interface(WSGI)が生まれました。
WSGIの役割
WSGIは、Javaサーブレットと同じように、WebサーバーからWebアプリケーションを切り離します。このようにすれば、Webフレームワークの作者たちはWebアプリケーションを実装する最善の方法は何かということだけを考えればよくなり、サーバーの実装の詳細はWSGIの「向こう側」の担当者に委ねることができます。
WSGIの目的はWebサーバーと簡単にインターフェイスする方法をWebフレームワーク開発者に提供することにあるのですが、WSGIはWebアプリケーションを作成する方法としてもかなり興味深いものです。Ian Bickingは、Pycon 2007での「WSGI: An Introduction」というプレゼンテーションで、WSGIを初期のCGIプログラミングになぞらえました。いろいろ問題はあるにしても、初期のCGIは、サーバーとアプリケーションをきれいに分離する優れたカプセル化手法だったということが言われています。サーバー側はいくつかの環境変数をまとめてアプリケーションの標準入力に渡すという役割を担い、アプリケーション側はデータ(通常はHTML)を標準出力に返していました。もちろん、CGIは重たくて遅いものでしたが、処理をうまくカプセル化してくれていました。
WSGI利用の基礎
インターフェイスが単純だという点で、WSGIはCGIに似ています。実際、戸惑うくらい単純です。Webアプリケーションのデプロイが難しいと思っている人たちは、たいていWSGIを知って驚きます。基礎的な例を挙げましょう。
def hi(environ, start_response): start_response('200 OK', [('content-type','text/html')]) return "HI!" from wsgiref.simple_server import make_server make_server('', 8080, hi).serve_forever()
このアプリケーションは "hi" という関数で、これは引数として環境(ディクショナリ)と、start_responseという関数を受け取ります。hiアプリケーションの最初の行、すなわちstart_response('200 OK', [('content-type','text/html')])
では、要求が適切に処理されたことを示すためにHTTP応答200を返し、後続のデータのMIME Typeがtext/HTMLであることをクライアントに知らせます。その後、hiアプリケーションはHTML(ここでは "HI!" という単純なもの)を返します。標準入力で環境を渡し、標準出力から応答を取得するというCGIのやり方によく似ています。
この関数は、完全なWSGIアプリケーションに必要なものをすべて備えています。hiアプリケーションをWSGIコンテナにプラグインして実行するのは簡単です。最後の2行でまさにそれを行っています。
from wsgiref.simple_server import make_server make_server('', 8080, hi).serve_forever()
筆者はPython 2.4からPython標準ライブラリに含まれているWSGIリファレンスサーバーを使用しています。これをFastCGI、AJP、SCGI、またはApacheコンテナに置き換えるのは簡単でした。このことからわかるように、これは一度書けばどこでも実行できる、プラグアンドプレイのWebアプリケーションです。
WSGIを使ったフォトギャラリーの作成
Hello Worldのレベルはこれで卒業し、次はもっと実用的なアプリケーションを作成してみましょう。2007年8月4日、筆者と妻の間に1人目の子供が生まれました(名前をWilliam Christopher McAvoyと言います)。それ以来というもの、我々は膨大な数の写真を撮ってきました。写真はすべて外部ハードディスクドライブの一群のフォルダにきちんと整理して保存しています。妻は祖父母にあげる写真を探すとき、筆者のコンピュータまで出向いてきて写真に目を通します。共有ドライブも試してみましたが、遅すぎて話になりませんでした。ファイルシステム上の膨大な写真を読み取るWebアプリケーションがないかと少し探してみたのですが、見つかりませんでした。既存のギャラリーはどれも写真をアップロードするものばかりで、もともと存在する一群のフォルダを想定しているものはありませんでした。
その後、旅行中に空港でしばらく時間をつぶしているときに、わりあい使いものになりそうなWSGIアプリケーションを思いつきました。Webパスをディレクトリパスに変換し、サムネールを動的に作成して、膨大なjpegのリストを簡単にブラウズできるようにする、というアイデアです。帰宅してから、このアプリケーションを自分のApacheデスクトップシステム上のmod_wsgiコンテナにプラグインすると、それは開発に使っていたPython 2.4付属のWSGIコンテナのときと同じように問題なく動作しました。
このアプリケーションの完全なソースは、筆者のGoogle Codeページから入手できます。このアプリケーションの肝心な部分はfsPicture
クラスです。「クラスだって? WSGIアプリケーションは関数だと思ってたのに」と言われるかもしれません。それもある意味正解です。WSGIアプリケーションは呼び出し可能な関数ライクのオブジェクトと考えられており、__call__という魔法のメソッドをオーバーライドする限り、それはオブジェクトであり得ると言えます。
これは単純そうに見えますが、実のところWSGIをいじり始めた段階では筆者も混乱し、そのために少し時間がかかりました。たとえば次のようにクラスを宣言し、
class Something(object): def __call__(self): return "Hi there!"
これを次のようにインスタンス化すれば、
s = Something()
s
をs()
という関数と同じような感覚で呼び出すことができます。これは次のようにs
関数を作成するのと機能的に同じです。
def s(): return "Hi there!"
これは素晴らしいことです。なぜなら、オブジェクトをWSGIアプリケーションとして作成できることを意味し、したがって関数をベースとしたWSGIアプリケーションを作成するより、ずっとスッキリしているからです。