2010-12-07

Python 3.x 対応のフレームワーク QP

Python Web フレームワークアドベントカレンダー の 7 日目担当の、ふるかわとおるです。よろしくお願いします。

あのですね。Python 3.x みんな使わないんですよねぇ。やっぱり、それなりのことをやりたくなると、サードパーティのライブラリに依存することになるんですが、そのライブラリが 3.x 対応じゃないと、なかなか乗り換えられませんよね。特にウェブの場合は、文字列まわりの変更の影響が大きいですし、WSGI の次期仕様である PEP444 が確定してないこともあったりで、軒並み Python 3.x 対応していません。http://python.org/3kpoll を見ると、Python 3.x 対応してほしいランキング 1位が Django ですね。私も1票投じているわけですが。

さてさて、そんなわけで、ここでは Python 3.x に対応しているウェブフレームワーク QP を紹介します。Mac OS X 10.6.5 でビルドした Python 3.2 を使いました。紹介といいつつも、ドキュメントがほとんどないので、必死でサンプルを読み解いた軌跡です。やべぇ、もう 23:22。

インストール

setuptools では、途中のビルドに失敗するので、Distribute を先にインストールします。

http://pypi.python.org/pypi/distribute からソースをダウンロードして、

$ python3.2 setup.py install

つづいて、

$ easy_install-3.2 durus
$ easy_install-3.2 qpy
$ easy_install-3.2 qp

durus は ZODB っぽいデータストレージ、qpy はテンプレートエンジンです。qp は Quixote の後継フレームワークなんですけど、23:27 になったので次。

Hello world!

qp のプロジェクトは、配置する場所が決まっています。/var/qp_sites とか。あるいは環境変数 QP_SITES に格納されたパスに置いておきます。

ここでは qp_sites/hello ディレクトリをまず作ります。必要なファイルがいくつか必要です。

  • qp_sites/hello/__init__.py ... からっぽで OK
  • qp_sites/hello/var ... 空っぽのディレクトリ。pid ファイルとかが置かれる。
  • qp_sites/hello/slash.py ... ここにコードかく
では、slash.py を見ましょう。

from qp.pub.publish import Publisher
from qp.fill.directory import Directory

class SitePublisher (Publisher):
    configuration = {"http_address":("", 8000)}  

class SiteDirectory (Directory):
    def get_exports(self):
        yield ("", "index", "top page", "Home page of hello")  # パス、メソッド名、名前、タイトルのタプル

    def index(self):
        return "hello, world"


ターミナルから
   qp -u hello
で、起動します。






テンプレートエンジン使う準備

変更するファイルがトリッキーです。まず、slash.py を slash.qpy にリネームして、以下のように編集します。

# qp_sites/hello/slash.qpy 

from qp.pub.common import header, footer
from qp.pub.publish import Publisher
from qp.fill.directory import Directory

class SitePublisher (Publisher):
    configuration = {"http_address":("", 8000)}

class SiteDirectory (Directory):
    def get_exports(self):
        yield ("", "index", "top page", "Home page of hello")

    def index [html] (self):
        header("Hello!")  # <html><head> ... とかを出力してくれる
        "<strong>Hello,</strong> <em>World!</em>"
        footer()  # </body></html>を出力してくれる


つづいて、__init__.py も編集。

#__init__.py 
from qpy.compile import compile_qpy_files
compile_qpy_files(__path__[0])


.qpy は、微妙に Python とはことなってて、def index [html] (self) あたりが拡張されています。テンプレートエンジン qpy はこのファイルをコンパイルしたものを使います。なので、テンプレート書き換えたら、qp を再起動します。

  qp restart

index メソッドがテンプレートになっていて、各行を評価した結果の文字列が、テンプレートの出力になります。
header() の戻り値、"<strong>Hello,</strong> <em>World!</em>"、footer() の戻り値が連結されて、レスポンスになります。




テンプレートと永続化を使う

わたくし、ちょうど1年くらい前にウェブ業界にはいりまして、データベースとか詳しくないんですよねぇ。なので、MySQL とかとつなぐとかめんどくさくって。qp なら durus というオブジェクト永続化の仕組みをつかえるので楽ちんです。ZODB を知ってる人ならなじめるらしいです。

# qp_sites/hello/slash.qpy

from qp.pub.common import header, footer, get_publisher
from qp.pub.publish import DurusPublisher
from qp.fill.directory import Directory
from durus.persistent import PersistentObject

class SitePublisher (DurusPublisher):
    configuration = {"http_address":("", 8000)}

    def count_up(self):
        d = self.get_root()
        d['count'] = d.get('count', 0) + 1
        self.commit()
        return d['count']

class SiteDirectory (Directory):
    def get_exports(self):
        yield ("", "index", "top page", "Home page of hello")

    def index [html] (self):
        header("Hello")
        "<strong>Hello,</strong> <em>World!</em>"
        "<p>"
        get_publisher().count_up()
        "</p>"
        footer()

データは SitePublisher クラスのプロパティとして格納されます。でデータは基本的にツリー構造になっていて、ツリーの根をとるのが、get_root() メソッドで辞書っぽいオブジェクトを返します。これに任意のオブジェクトを追加して、commit() メソッドを実行すると、データが保存できます。上の例では、 count_up() メソッドがそれです。



てな感じですね。

まとまらないまとめ

テンプレートが 似非 Python コードであること、データの保存がオブジェクト永続化であること、あたりが、メジャーどころと大きくことなる QP ですが、ちょっと気になっています。durus はマルチスレッド、マルチプロセスに対応している雰囲気です。そんなわけで、QP と Python 3.x 、これからも気にしていこうと思います。

つぎは、Python 界のイケメン地ビール愛好家 @MiCHiLU さんにお願いしまっす。