読者です 読者をやめる 読者になる 読者になる

Flask 入門日誌 ハロワ編

Flask 入門日誌目次 - にっき

FlaskはPythonのWeb開発するためのマイクロフレームワークです。Pythonの極小WAFでは一番有名な子のはずなので、この子はとってもいい子だよ!的な紹介はいらんでしょう。本シリーズではチュートリアルを読んで考えたこと、つまづいたところなどを適当に垂れ流していきます。

なんかプログラミングやるならウェブアプリとか作れた方がいいのかなぁ(やったことない)と思ったのでやってみることにした。

とりあえずハロワ

hello.pyとかそういう感じのファイルに書く。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, World!"

if __name__ == '__main__':
    app.run()

サーバを起動する。

$ python hello.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

表示された http://127.0.0.1:5000/というローカルアドレスに適当なブラウザでつなぐと、Hello, World文字列がこんにちはしてくれる。またアクセスするとログ出力が流れる。

127.0.0.1 - - [22/Jan/2017 10:35:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [22/Jan/2017 10:35:36] "GET /favicon.ico HTTP/1.1" 404 -

favicon.icoにGETがきてる。これはブラウザ氏の仕様なんだろうか。

調べるとこんなSOにそれっぽいのがあった。

How to prevent favicon.ico requests? - Stack Overflow

試しに、こんな感じで書いてみた。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return """
    <html>
    <head>
    <link rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\">
    </head>
    <body>Hello, World!</body>
    </html>
    """

if __name__ == '__main__':
    app.run()

するとfaviconへのpayloadが抑制された。へぇー。へぇー。

$ python hello.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [22/Jan/2017 10:42:28] "GET / HTTP/1.1" 200 -

Flaskとは関係ないのだけど、なんかおもしろい。

最初のプログラムはなにをしているのか?

Quickstartに書いてある *1

1, 3行目。

from flask import Flask

app = Flask(__name__)

見たまんま、Flaskクラスをインポートしてインスタンス化している。

最初くらい詳しく見ていこうと思うのでAPIドキュメントを適当に訳出。

class flask.Flask, API — Flask Documentation (0.12)

Flaskオブジェクトはセントラルオブジェクト(central object, 中央オブジェクト)としてふるまう。Flaskオブジェクトが一度生成されると、セントラルレジストリ(中央登録者)としてビュー関数、URLルール、テンプレート設定などさまざまなものを登録するものとして振るまう。

__name__を明示的に渡す理由については、Flaskに自分が作っているアプリが属しているものを明確にする意味あいがある。 Flaskは__name__を利用して、ファイルシステム上のリソース、つまり静的ファイルやテンプレートなどを探す。またよりよいデバッグ情報を出すため、拡張機能などが__name__を利用する。

それゆえ、Flaskに__name__を与えるというのは重要な意味を持つのである。例えば、単体のモジュールファイルでアプリを作っている場合、間違いは起こらない。

しかし、もしパッケージを使っている場合、パッケージの名前をハードコードすることが推奨される。例えばyourapplication/app.pyというアプリを作っているとき、次の2つの方法のうちどちらかをとる必要がある。

app = Flask('yourapplication')
app = Flask(__name__.split('.')[0])

なぜそうする必要があるのか?アプリケーションはたんに__name__を渡しても(リソースを探索するという点においては)間違いなく動作する。しかしながらデバッグは苦行のようになる。いくつかのデバッグ拡張はアプリのインポート名に依存している。例としてFlask-SQLAlchemy拡張は、デバッグモードでSQLクエリが発行されているアプリ中のコードを探している。インポート名が適切に書かれていないとデバッグ情報は消失する。

正直__name__やパッケージ周りの話は、実際にアプリ作ってimportしてデバッグして…という作業をしていかないと感覚が掴めないと思う。あとで苦しまなくて済むように頭の片隅においておこう。

次に進む。

@app.route("/")
def hello():
    return "Hello World!"

デコレータでhelloをくるんでいる。routeデコレータの引数/は関数を発火するURLを示しており、ここではトップレベ/にアクセスするとhelloの返す文字列"Hello, World"が返される、という寸法である。

デコレータについての説明を始めると長いので省略。Pythonのデコレータを理解するための12Stepなんかをみるとわかりやすいと思う。個人的にはPythonが言語機能として持っている弱いメタプログラミング機構だと思っている(認識が適当…)。

さて、ドキュメントには次のような説明がある。

4.The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user’s browser.

4.この関数には、その特定の関数のURLを生成するためにも使われる名前が付与される。関数はユーザのブラウザに表示したいメッセージを返す。

そもそも英文の意味が取りづらいと思う。"The function"というのは前段3.の"our function"のことで、ここではhelloである。色々ドキュメントを探した結果、app.view_functions['hello']を見ると、Flaskインスタンス<function hello at 0x10c595840>という感じでビュー関数が登録されているのを確認した。ビュー関数をFlaskオブジェクト内の辞書に登録されている部分を"is given a name"と説明したのだろうか。*2

最後にapp.run()でローカルサーバを起動できる。ただし、flask最新版(0.12)ではこの部分は書かずにflask runコマンドでサーバを起動することができる(起動するファイルはexport FLASK_APP=hello.pyという感じで環境変数に登録しておく)。

だらだら書いてたら記事が間延びしてきたのでここらで一旦切る。

*1:このへんは日本語訳があるのでスキップでいいんでは?と思ったが、確認したら翻訳はバージョンが古くて(0.5.1と0.10)英語の最新版(0.12)に追従してない…。ハロワの説明については文章構造をいじってるだけなので別に問題ないけど、のちのち齟齬が出てきそうな雰囲気がある。あとなんか翻訳されたサイトが2種類ある…これはせっかくBitbucketやらGitHub使ってんのに協力せんの?って感じだ。またどちらのサイトも後半部は翻訳されないまま放置されているらしく、APIドキュメントなども訳出されていないので、日本語になってない部分に触れつつやっていこうか。

*2:それはそれとして、この部分についての他の日本語訳を見たらちょっと悲しい気持ちになった。あまり人のことを強く言える立場ではないが。