futoase

よろしくお願いします。

俺 AdventCalendar 17日目

Werkzeug

  • Werkzeug とは、WSGIのアプリを作成するためのライブラリ。
  • ドイツ語で、「ツール」という意味。
  • Flask で利用されている。
  • 公式サイトに載っていて知ったが、tipfy というフレームワークでも利用されているようだ。

試用する

試してみる

  • wsgiAPIに準拠しているので、以下の形で書くことができる。
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple

def application(environ, start_response):
  response = Response('Hello World',  mimetype='text/plain')
  return response(environ, start_response)

if __name__ == '__main__':
  run_simple('127.0.0.1', 5000, application, use_debugger=True, use_reloader=True)

実行してみる

f:id:syanbi:20120108002936p:plain

f:id:syanbi:20120108003319p:plain

Tutorial に沿ってWebアプリを作成していく。

アプリケーションの構成

  • Tutorialのアプリケーションは、URLを短縮化するアプリ。
  • 短縮化したURLについてはRedisを利用して保存する。

Tutorialを始める前に

  • redisを入れ、serverを起動する
brew install redis
redis-server /usr/local/etc/redis.conf

ファイル・ディレクトリ構成

.
├── shortly.py
├── static
│   └── style.css
└── templates
    ├── layout.html
    ├── new_url.html
    └── short_link_details.html

ファイル・ディレクトリの説明

shortly.py

static ディレクトリ

  • 静的ファイルを置く。
  • shotly.py の中で以下の形で /static サブフォルダにアクセスした場合はapplication root直下の static ディレクトリにルーティングするようにしている
if with_static:
  app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
    '/static': os.path.join(os.path.dirname(__file__), 'static')
  })

templates ディレクトリ

  • テンプレート用ディレクトリ
  • Jinja2をWerkzeugはサポートしているのと、TutorialでもJinja2に沿った形でテンプレートを作成している

Tutorialに出ているcode snipet

  • Werkzeugに関係のあるコードを取り上げないかもしれない

アプリケーションのルーティングについて

Shortly クラスに以下のメソッドを用意する形になっている

  • __init__ 以下に以下のコードを追加する。
self.url_map = Map([
    Rule('/', endpoint='new_url'),
    Rule('/<short_id>', endpoint='follow_short_link'),
    Rule('/<short_id>+', endpoint='short_link_details')
])
  • これは見ればわかるが、Rule クラスのコンストラクタ第一引数が アプリケーションでルーティングを行うパス、 endpoint がルーティングに対応するクラスメソッドとなる

ルーティングに対応するクラスメソッド

  • 実際に宣言されているクラスメソッドは以下の通り。
def on_follow_short_link(self, request, short_id):
  …
def on_short_link_details(self, request, short_id):
  ...

ルーティング時に指定しているメソッドと合わない?

  • dispatch_request メソッドで、'on_' と ルーティング設定時に指定したメソッド名を合わせて、getattrで自分自身のメソッドとして呼び出す形にしている。
def dispatch_request(self, request):
  adapter = self.url_map.bind_to_environ(request.environ)
  try:
    endpoint, values = adapter.match()
    return getattr(self, 'on_' + endpoint)(request, **values)
  except HTTPException, e:
    return e
  • dispatch_request メソッドは wsgi_app メソッドで呼び出し。
def wsgi_app(self, environ, start_response):
    request = Request(environ)
    response = self.dispatch_request(request)
    return response(environ, start_response)
  • wsgi_app メソッドは __call__ フックメソッドで呼び出し。
def __call__(self, environ, start_response):
  return self.wsgi_app(environ, start_response)

werkzeug.routing の動作を見る

  • dispatch_request メソッドで何を行なっているのか、を確認するために werkzeug.routing 以下にあるクラスの動作を確認してみる。

そして

先ほどあげた 'on_' + メソッド名の形でメソッド名を取得する箇所にて解決をする。

return getattr(self, 'on_' + endpoint)(request, **values)

http://localhost:5000/10 であれば 'on_' + 'follow_short_link' メソッドが呼ばれる。

snipet

  • ipythonにて実行した結果は以下。

f:id:syanbi:20120117192240p:plain

使ってみたが

  • 通常のWebアプリを作成するにはちょっとものが大きい感じがする。
  • FlaskやらBottleやら、web.pyなどもうちょっと軽いものを選択するか、wsgirefを元にWSGI インタフェースに沿った形で自作したほうがいいのかも。
  • ライブラリの構成、オブジェクトのやり取りについてはコードリーディングする価値は高いので読むべき。
  • 自作Frameworkを作成するときに使えるか(時間的な制約も含めて旨みがあるか)は、サニタイズ対応をしていたり、Jinja2をテンプレートエンジンとして利用できるようになっていたりするのでコードの一貫性を保った形で自作Frameworkを作りたい(Werkzeug色で埋め尽くす)のであれば選択肢にあがる。

Copyright (c) 2013-2017 Keiji Matsuzaki