Falconソースコードリーディング -その1-

Posted by rhoboro on 2016-12-11

(第71回)Python mini Hack-a-thonで行った内容の個人的な備忘録です。
書いていたらついつい長くなってしまったので、何回かに分けます。

このイベント、Hack-a-thonと書いていますが、各々が好きなことをやるイベントです。 今回は、先日Qiitaで見つけた、Falconという速いらしいWebフレームワークのソースコードをPythonの勉強がてら眺めてみました。

Falconとは

情報はこちらにまとまっています。

ざっと見たところ下記のような特徴があるようです。

  • ミニマルで速いWSGIアプリケーション
  • HTTP and the REST architectural styleな設計
    • Resourceクラスを定義してon_geton_postを実装する
  • APIサーバー向けに絞った機能だけを提供(テンプレート機能や認証などはなし)
  • テストコードとドキュメントがしっかりある(コメントも豊富)

ついでにソースコード(*.py)のファイル数や行数なども調べてみました。
ちょっとしたPythonの勉強にはちょうどいいサイズ感ではないでしょうか。

[alpaca]~/github/falcon/falcon % pwd
/Users/rhoboro/github/falcon/falcon

# ソースコードは55ファイル
[alpaca]~/github/falcon/falcon % find . -name "*.py" | wc
      55      55    1181

# トータルで9500行程度。一番大きなファイルでも1351行。
[alpaca]~/github/falcon/falcon % find . -name "*.py" | xargs wc | sort -r | head
    9439   39313  343961 total
    1351    7370   60395 ./errors.py
    1321    5517   50907 ./request.py
     718    3271   27566 ./response.py
     683    2913   28151 ./api.py
     652    2664   24076 ./testing/client.py
     457    2120   16512 ./util/uri.py
     363    1394   13628 ./routing/compiled.py
     334     837    9109 ./bench/bench.py
     318    1144    9566 ./util/misc.py

ソースコードを読んでいく

ざっくりと概要がわかったところで、下記の流れでソースコードを読んでみました。

  1. QuickstartTutorialを読んでざっくりと使い方を知る
  2. エントリポイントがどこかを調べる
  3. そこからどういう処理が行われているかを調べる
  4. せっかくなので、速い理由になってそうなところを調べる

エントリポイント

Falconを利用したアプリケーションの最小構成は、ドキュメントにあるとおり。

import falcon

api = application = falcon.API()

こちらのfalcon.API()がWSGIアプリケーションになります。
でもほぼろは「WSGIアプリケーションとは」をそもそもちゃんと理解していなかったので、WSGIについても調べました。

Python Web Server Gateway Interface (WSGI)

声に出すときは「うぃすぎー」「うぃずぎー」あたりであれば恥ずかしくないです。

WSGIの詳細はPEP333をご覧ください。(どうしても日本語でという方はWikipediaを)
とても簡単にいうと、WebサーバーとWebアプリケーションを繋ぐ部分(インターフェース)の仕様です。 お互いがWSGIに準拠していれば、Webサーバを変えずにWebアプリケーション側のフレームワークを変えたりできるという利点があります。 WSGI準拠のWebサーバーとしては、gunicornuWSGIが有名でしょうか。 Webアプリケーション側だとDjangoFlaskPyramidBottleなど有名どころはほぼ対応しています。

で、WSGIアプリケーションがやらないといけないことはざっくりとこんな感じです。

  1. Webサーバーにリクエストが来ると、WebアプリケーションのCallableなオブジェクトが呼び出される(falcon.API())
  2. このとき、Callableなオブジェクトにはパラメータとして、いろいろな環境変数とstart_responseが渡される
  3. Webアプリケーションは、(必要な処理を行った後)パラメータで渡されたstart_responseを呼び出し、ステータスコードとヘッダーを渡す
  4. その後、iterableなオブジェクトを返り値として返す

Webサーバーはこれらの情報からレスポンスを生成し、クライアントに返すという流れになっています。

falcon.API()

Falconでエントリポイントとなっているfalcon.API()のソースコードはfalcon/api.pyです。

class API(object):

    ~省略~

    def __call__(self, env, start_response):  # noqa: C901

    ~省略~

        # Return the response per the WSGI spec
        start_response(resp.status, headers)
        return body

__call__メソッドは、オブジェクトを関数のように呼び出し可能なオブジェクトにしてくれる特殊メソッドです。
WSGIサーバーにAPIクラスのインスタンスを渡し、リクエストがきた際にenv, start_responseをパラメータとしてこのインスタンスを呼び出してもらいます。 そして、ごにょごにょと処理を行った後、start_response(resp.status, headers)でステータスコードとヘッダーを渡し、body(これはiterableなオブジェクト)を返しています。

すべてのリクエストでこの処理が毎回行われることになります。
ちゃんと上述したWSGIアプリケーションに沿った動きになっていますね!

次回に続く

WSGIの説明しかしてない気もしますが、今回はここまで(笑)
次回はもう少しソースコードを読んでいて気になったところをピックアップするつもりです。

HTTPはステートレスなものなので、いまの状態などを意識しないでいいのは普段モバイル開発をしている身としては楽ですね!
スマホアプリはそれ自体がステートフルなものなので、常に状態を気にせずにはいられない...orz

tags: Github, Falcon, Python