memo

思いついたこと、やってみたことをテキトウに残していく。

HEAD / archives / 2013-11 / 2013-11-14.rst

State で request 持ち回すような Python micro framework 書いてる

TL;DR

Yet another micro framework 書いてる。 State monad っぽい仕組みで request object (その他 グローバルな object) を持ち回せるのが特徴。ベースは twisted.web 。

https://bitbucket.org/nakamura/tsumuji

State で request 持ち回すような Python micro framework 書いてる

Python のウェブアプリケーションフレームワークで、 request object を持ち回す方法は自分が知っているのだと3種類ある。

  1. 引数として明示的に渡す

    Django が採用しているような方法。

    Request object は request handler の引数として渡され、 request object を必要とする関数にはやはり引数として渡す。

  2. Thread local を使う

    Flask が採用しているような方法。

    Request object を必要とするものはどこでも thread local から request object を取り出せる。

    (多分。 Flask 使ったことないです。違ったらすいません。)

  3. object の attribute に入れる

    Tornado が採用しているような方法。

    Request object は request handler object の attribute にセットされ、 self.request のような形でアクセスする。

さて、これらの方法ではなく state を使ったらどうだろうか、 というのをやってみたかったので、そんなような framework を書いている。

State というのは Haskell でよく見るアレですね。 内側からだとグローバル変数のように状態にアクセスできるけど、 外から見ると初期状態受け取って最終状態と結果返すただの関数、というやつ。

今のところ以下のような感じで書けるようになっている:

from tsumuji import Application, \
    getApplication, getRequest, urlFor, \
    statefulCallbacks, returnValue, writeTemplate, redirect


app = Application()

# use app as a global datastore
app.comments = []


@statefulCallbacks
def getComments():
    app = yield getApplication()
    returnValue(app.comments)


@app.route('/')
@statefulCallbacks
def index():
    comments = yield getComments()
    yield writeTemplate('guestbook.html', comments=comments)


@app.route('/create')
@statefulCallbacks
def create_comment():
    request = yield getRequest()

    name = request.args['name'][0].decode('utf-8')
    text = request.args['text'][0].decode('utf-8')

    comments = yield getComments()
    comments.append((name, text))

    next_url = yield urlFor('index')
    yield redirect(next_url)


app.run(8080)

Decorator と generator の力により、半暗黙的に状態を引き回している。

上述したように外側から見れば状態を受け取って状態と結果を返す関数なので、 以下のようにして呼び出せる:

d = index().run(dummyState)

def callback((result, state)):
    print (result, state)

d.addCallback(callback)

Deferred が返っているのはベースの Twisted と合わせるため。

まだまだバグが残っていそうだけど、 ぼちぼち昔書いたアプリケーション移植してみて使い勝手を見ようと思う。

何で Twisted ベースなの?

引数で持ち回すのが面倒くさくなっても、 thread local に逃げられない framework だから。

powered by blikit