Sanic+Ginoの開発環境ボイラープレート

Python Advent Calendar 2018 - Qiitaの12/13の記事です。

Sanicって良いですよね。速いし、シンプルだし、ドキュメントも充分です。
もうすぐ2019年になりますし、Pythonでサーバーを書くならasyncioを使ってノンブロッキングに実装したいものです。
個人的にWebフレームワークはDjangoみたいなフルスタックなものよりFlaskみたいな小さいものから始めて適宜追加していく方が全体を把握しやすいので好きです。
しかしながら、シンプルといってもウェブアプリケーションを作るなら

  • 新しいURLルーティングが増えた時に整理しやすいようにしておく
  • テストを書きやすいようにしておく
  • コンフィグ等を拡充しやすいようにしておく
  • DBとの接続やマイグレーションを出来るようにしておく

といったことは治安を守って快適に開発していくためには最低限必要なことかなと個人的には思います。
SanicのHello World Exampleから始めるのではなく、↑が揃ったSanic用のボイラープレートでもあればなあと考えていたので最近作った開発環境を晒したいと思います。

Hello World Example

from sanic import Sanic
from sanic.response import json

app = Sanic()

@app.route('/')
async def test(request):
    return json({'hello': 'world'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

作ったもの

こちらになります。

github.com

以下はこれの解説になります。 なお、使っている主な技術を先に列挙すると

  • Python3.7
  • Docker
  • docker-compose
  • Pipenv
  • Sanic
  • tox
  • pytest
  • Postgre SQL
  • Alembic
  • Gino
  • SQLAlchemy

といった感じです。

とりあえず動かす

https://github.com/sasaujp/sanic-gino-boilerplateをコピーなりcloneなりして

$ docker-compose up -d
$ pipenv install --dev
$ pipenv run tox

を実行するとdocker-composeでPostgreSQLが立ち上がり、pipenvでパッケージがインストールされ、toxでテストが起動し、一通りのコードが実行されます。 サーバーを起動する場合は最後を

$ pipenv run python main.py

とするとポート8000でサーバーが起動します。

URLルーティング

/myapp/blueprints を見てください。

/myapp/blueprints/__init__.pyを見ると

from sanic import Blueprint
from .api import api
from .pages import pages


bp = Blueprint.group(api, pages)

apipages入れ子にしています。

/myapp/blueprints/api/__init__.pyを見ると

from sanic import Blueprint
from .hello import hello
from .todo import todo


api = Blueprint.group(hello, todo, url_prefix='/api')

hellotodo入れ子にしていて、url_prefixが設定されています。

/myapp/blueprints/api/hello.pyを見ると

from sanic import Blueprint, response

hello = Blueprint('hello_api', url_prefix='/hello')


@hello.route('/world')
async def hello_api(request):
    return response.json({'hello': 'world'})

Hello, WorldなAPIがあります。
SanicのBlueprintは入れ子にすることができ、url_prefixでpathの設計が出来ます。
トップのBlueprintはここで登録されているため、 このAPI/api/hello/worldでアクセスすることができます。

テスト

toxを使用し、pytestでテスト、flake8, mypyで文法チェックをしています。
よく使われている方法だと思われ、検索で出てくる情報も豊富なので細かく解説はしませんが
tox.iniで設定をしています。

コンフィグ

myapp/config.pyにあります。
今はまだDBの設定を書いているくらいであまり書くことはないのですが、tox.iniでセットしている環境変数でテスト用のコンフィグに分岐するようにしていて、普段とDB名が変わるようにしています。
/myapp/__init__.pyで登録しています。

DB関連

マイグレーション

マイグレーションとはDBのスキーマの変更をバージョン管理するためのもので、Alembicを使っています。
これもよく使われている技術で情報も豊富なので細かく解説はしません、しませんが
このボイラープレートを開発に使う場合は/alembic/versionsの中身を消した上で始めると良いと思います。

Gino

Ginoは今回使った技術の中でそこまでメジャーではない部類になるのかなと思います。
ノンブロッキングなWebサーバーを実装するにあたってSQLAlchemyとDBを非同期IOで橋渡しをしてくれるものが必要でした。
GinoはPostgre SQLにしか対応していませんが、とても使いやすい印象を受けたので採用しています。
Ginoの初期化は/myapp/blueprints/events.pyでサーバー起動時に行なっています。
具体的な操作は/myapp/blueprints/api/todo.pyのTodo APIで行なっています。
挙動のテストは/tests/http/test_todo.pyで行なっています。
SQLAlchemyを使っている人であればかなり手に馴染むのではないかなと思います。

終わりに

この記事、および今回作ったものは個人開発で出来てない部分の自戒のために書いたのは正直あります。
プライベートでは勢いで書き始めて辛い思いをすることがほとんどなので...。
asyncioや非同期IOは難しい印象を受ける方も多いと思いますし、実際慣れるまでは挙動の想像がしづらかったりするのですが ちゃんと書けば性能として帰ってくるし気分も良いので、これから挑戦してみようという方の一助になればと思います。