適当おじさんの適当ブログ

技術のことやゲーム開発のことやゲームのことなど自由に雑多に書き連ねます

Flask 1.0 雑ピックアップ

Flaskが1.0になったので変更点や追加機能を雑にまとめていきます。全貌をちゃんと見たい人は、リリースノートや関連するプルリクを見てもらえればと思います。リリースノートの #から始まる数字のリンク が対応するプルリクへのリンクになっています。

Flask Changelog — Flask 1.0.2 documentation

この記事では以下について雑に触れます。

flask run コマンドの変更

Flaskアプリケーションは、flask run というコマンドでアプリケーションを実行できます。バージョン1.0になり、コマンドにいくつかの変更が加えられました。

python-dotenvとの連携

python-dotenv がインストールされていると、flask runFlask.run() 実行時に .env もしくは .flaskenv を自動的に読み込んでくれるようになりました。

ファイル 内容
.env APIキーなどの機密情報を含める。リポジトリにはコミットしない。
.flaskenv 公開しても問題ない情報を含める。リポジトリにコミットする。

以下のようにアプリケーションに .flaskenv を作成し、flask run を実行すると2つの値が読み込まれます。

flask-app/
├── .flaskenv
└── flaskapp.py
# .flaskenv
FLASK_APP=flaskapp
FLASK_RUN_PORT=3000

自動読み込みを無効化する方法など、より詳細な設定方法が知りたい方は公式ドキュメントを参照ください。

Command Line Interface — Flask 1.0.2 documentation

FLASK_APPが設定されていない場合の挙動

FLASK_APP の指定なしにflask run が実行された場合、app.py もしくは wsgi.py が自動的に実行されるようになりました。どちらも存在する場合、wsgi.py が実行されるようです。 わざわざ FLASK_APP を指定するのも面倒くさいので、app.py を使っていこうと思います。

Flask.run() のスキップ

flask run コマンド実行時に、Pythonスクリプト中の Flask.run() がスキップされるようになりました。スキップされている場合は Silently ignoring app.run() because the application is run from the flask command line executable. といった警告が出力されます。

バージョン0.12までは、flask run コマンドで実行するとコード内の Flask.run()まで実行されてしまいます。それに関する混乱を避けるためにこの修正が加えられました。

flask routes コマンドの追加

flask routesコマンドで、Flaskアプリケーションのルーティング情報を一覧できるようになりました。次の例では、Flaskアプリケーション、blueprintにそれぞれルーティングを登録、表示しています。

from flask import Flask, Blueprint

app = Flask(__name__)

@app.route('/hello')
def hello():
    return "get hello"

@app.route('/post', methods=['POST'])
def post_hello():
    return "post hello"

bp = Blueprint('bp', __name__)
@bp.route("/bp_hello", methods=['GET', 'POST'])
def bp_hello():
    return "blueprint"

app.register_blueprint(bp)

GETやPOSTといったHTTPメソッドの情報も表示されます。blueprintの場合は、blueprintの名前とメソッドが表示されます。

Endpoint     Methods    Rule
-----------  ---------  -----------------------
bp.bp_hello  GET, POST  /bp_hello
hello        GET        /hello
post_hello   POST       /post
static       GET        /static/<path:filename>

HTTPExceptionのエラーハンドリングの変更

errorhandler(HTTPException) で、HTTPExceptionのサブクラスの例外をキャッチできないバグが修正されました。

from werkzeug.exceptions import HTTPException

app = Flask(__name__)
app.errorhandler(HTTPException)
def http_exception(e):
    return "occured http exception"

Flaskのerrorhandler は指定した例外のサブクラスの例外が発生した場合にもキャッチできます。バージョン0.12までは、NotFoundForbidden といったHTTPExceptionのサブクラスの例外を、errorhandler(HTTPException) でキャッチできませんでした。この現象の詳細は、過去の記事(HTTPExceptionのサブクラスの例外発生時の挙動の項目) に記載しています。

この修正でHTTPExceptionのサブクラスの例外発生時、同一のエラー画面にリダイレクトするのが楽になりました。

Flask.loggerに関する変更

Flask.logger.handlers にハンドラーが1つだけ追加されるようになりました。最初にloggerにアクセスしたタイミングでloggingの設定がされていなかったら、実行環境に応じてハンドラーが追加されます。

バージョン0.12までは、デフォルトでdevelopment用とproduction用の2つのハンドラーが登録されていました。そして、実行時の環境に応じてハンドラーが選択されていました。必要なハンドラーだけが登録されるようになり、挙動がシンプルになりました。

詳細な設定方法などは公式ドキュメントを参照してください。

Logging — Flask 1.0.2 documentation

ResponseオブジェクトのJSONサポート

Response.get_json()で、Responseオブジェクトに含まれるJSONデータを辞書型で取得できるようになりました。この変更により、Request と Response の双方のクラスで、get_json()is_json() が使えるようになりました。バージョン0.12までは、flask.json.loads()が必要でした。

from flask import Flask, jsonify

app = Flask(__name__)
@app.route('/test', methods=['POST'])
def test():
    return jsonify(result='test value')

with app.test_client() as c:
    json_data = c.post('/test').get_json()
    assert json_data['result'] == 'test value

公式ドキュメントのJSON APIをテストする方法のところに具体例が掲載されています。

Testing Flask Applications — Flask 1.0.2 documentation