Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

「それZopeだよ」って言われたので作ったもの

「それZopeだよ」って言われたので作ったもの

URLディスパッチャーとか飽きたので、もっと他のことがしたかった。 URL(リクエストのPATH INFO)にマッチしたパターンに対応するビューなど言ってないで、 より汎用的な構造はないかと考えていた。

思いついてつぅいーとしてた:

URLディスパッチャとか無しで、「リクエストオブジェクトを受け取り、レスポンスオブジェクトを返す、呼び出し可能オブジェクト」が、自身のパスにマッチしないリクエストであれば他の呼び出し可能オブジェクトに移譲するっていう何かを妄想した ( https://twitter.com/hirokiky/status/312183468127834113 )

というと「それZopeっていうんだよ」と教えてもらった。

あとは Traversal というのもあるらしかった。

今日、ちょっとTraversalを試して、分かった気になったりもしたけど、 結局自分で書いてみないと納得いかないので、書いた。

書いたもの

これ

ただ「自身のパスにマッチしないリクエストを移譲する」のではなく 「子を呼ぶ条件にマッチすると、それに移譲する」ものを書いた。

node というデコレーターを使って、どのWSGIアプリケーションが呼び出されるかを定義する。 gistにもあるサンプルを下にそのまま貼ったので、それで説明していく。

@wsgify
def node2(request):
return "OK. I'm node2"

@wsgify
@node({'a': 'node1', 'b': 'node2'})
def node3(request):
return "OK. I'm node3"

@wsgify
@node({'a': 'node2', 'b': 'node3'})
def node1(request):
return "OK. I'm node1"

if __name__ == '__main__':
httpd = make_server('', 8080, node1)
httpd.serve_forever()

各関数に付いている node というデコレーターが今回書いたもの。

ここではまず node1 が呼ばれる。けど @node の引数に渡されている辞書とPATH_INFO をみてマッチするものがあれば、さらにその子が呼ばれる。 例えば /a/ にアクセスされている場合、 node1 のマッピングに a が存在しているので node2 が呼ばれる。 node2 には @node がないのでそのままレスポンスが返される。

マッチするものがなかったら (例えば /c/) デコレートされている関数 (この場合 node1) がそのまま呼ばれる。

/b/a/ という場合、まず /b/ にマッチする node3 が呼ばれる。 1度呼び出しがあると次の @node は / 区切りで、その隣を見る。ここでは a 。 node3 では a に対して node1 が当てられているので /b/a/ では最終的に node1 が呼ばれることになる。

ちなみに辞書の値には関数の文字列を与えればよく、呼び出し時に読み込まれるので、関数を書く順番には 気を遣わなくていい。

拡張するなら

今回は辞書と、それに path_info がマッチしているかをみているだけだったが、 リクエストオブジェクトを受け取ってブール値を返す関数を与えて、その返り値が True の 場合に子を呼び出すようにすれば、どのような条件にも対応できる。 あるいはそのような関数のタプルを渡して、それらがすべて True の場合に呼び出すと、 PyramidのPredicateっぽくなるかもしれない。

できれば単にデコレーターで受け継ぐだけじゃなくて、その間に処理を挟みたい。 他の呼び出しがある前に、リクエストオブジェクトに対して何らかの処理を すればいいだけなので、それでミドルウェアの実装もできそうではある。

あとは「自身にマッチしなければ移譲する」ものを作りたかったので、それについて考えるのもいいかも しれない。