秀和システムより出版されている『Pythonプロフェッショナルプログラミング 第2版』を読んでPythonパッケージングの仕組みなどについて勉強している。
で課題として登場するFlaskを使ったWebアプリケーションをPythonパッケージとして作成する方法について、書籍のサポートページなどからサンプルページを探してみたものの、見付からなかったので1から作ってみることにした。何点か書籍の内容に対してアレンジを加えたので、併せて紹介したい。
コード全体はGitHub、PythonパッケージはTest PyPI Serverというパッケージリポジトリで公開している。
Test PyPIというのは名前の通りPyPI公開前の検査・学習用のサーバで、機能に関しては本番用PyPIと全く同じものが利用できる。
Test PyPI Server解説ページに書かれている通り、pipのオプション -i
でパッケージインデックスとして https://testpypi.python.org/pypi
を指定することで、Test PyPI上で公開したパッケージからインストールが可能になる。この時、課題guestbookアプリの依存しているFlaskはTest PyPIサーバからは見付けられないため、先にFlaskをインストールしておく必要がある。
# 先にFlaskをPyPIサーバからインストール
$ pip install Flask
# guestbookアプリをTest PyPIサーバからインストール
$ pip install -i https://testpypi.python.org/pypi raimon49.guestbook
次のような点を書籍の内容とは違った方法に変更した。いずれも2015年現在で主流に近い部分と考えられるが、最後のTest PyPIについては書籍中でもオプションの選択肢として提示されており、特別にアレンジした内容ではない。
書籍ではUbuntuサーバの中で最新の2.7系Pythonをビルドする方法が紹介されている。この辺りはマルチバージョンにPythonを切り替えら得られるpyenvを使った方が管理が楽であると考え、pyenv経由でPython本体をビルド・利用するようにした。
また、virtualenv環境もpyenvコマンドを通して透過的に管理・利用できるため、READMEの内容もpyenvを中心とした手順に変えている。
構成管理にMercurialはやや特殊だと感じたため、Gitを採用するよう変更した。
リポジトリのホスティングサービスは書籍の通りにBitbucketを使うことも考えられる(BitbucketではGitリポジトリのホスティングもサポートしている)が、ホスティング先もGitHubを使うことに変更した。
書籍との主な違いは構成管理上の無視設定を記述するファイルが .hgignore
から .gitignore
に変わる程度で、大きな違いは無い。
他にもGitHubを使うメリットとして、Add A Licenseといった便利な連係サービスからライセンスファイルの自動生成ができる。
書籍で紹介されているVirtualBoxと自宅PCでのVirtualBox環境との間で、ネットワーク設定がやや異なっていたので、いっそコマンドラインオプションで指定可能にしようと考えた。
Python 2.7には標準ライブラリとして強力なargparseモジュールが付属しているので、これを使ってやれば簡単に実現できる。
import argparse
from flask import Flask, request, render_template, redirect, escape, Markup
NETWORK = '127.0.0.1'
PORT = 8000
application = Flask(__name__)
def parse_args():
parser = argparse.ArgumentParser(
description='A guestbook web application.')
parser.add_argument('-v', '--version',
action='version',
version='guestbook 1.0.0')
parser.add_argument('-n', '--network',
default=NETWORK)
parser.add_argument('-p', '--port',
type=int,
default=PORT)
return parser.parse_args()
def main(debug=False):
args = parse_args()
application.run(args.network,
args.port,
debug=debug)
if __name__ == '__main__':
main(debug=True)
上記の実装によりコマンドラインオプションが指定されていたらそちらを使うようになったため、最終成果物の guestbook
コマンドでは、自由にネットワークとポート番号を変更してFlaskアプリケーションを起動できる。
# ネットワークとポート番号を変更して起動
$ guestbook -n 192.168.56.100 -p 5000
書籍では主なCIツールとしてJenkinsの使い方が紹介されている。GitHubではCI as a ServiceとしてTravis CIと組み合わせる方法がメジャーであるため、こちらを利用してPEP8に準拠したPythonコードが書けているかチェックするようにした。
チェックツールにはpytest-pep8を使い、非準拠コードの修正にはautopep8を使った。
# ツールのインストール
$ pip install pytest-pep8 autopep8
# コードのチェック
$ py.test -v --pep8 guestbook
# 非準拠コードの修正
$ autopep8 -i guestbook/__init__.py
$ git commit -am 'Fix PEP8: E302 expected 2 blank lines'
これらのチェック用ツールへの依存関係は後述のpip-toolsで dev-requirements.txt
というファイルにまとめ、Travis CIの設定ファイルでインストールされるように指定する。
language: python
python:
- "2.7"
install:
- pip install -r dev-requirements.txt
script:
py.test -v --pep8 guestbook
Travis CI側のWeb UIで今回のGitリポジトリと連携させるよう選択するとpushの度に自動でチェックが走るようになる。
書籍でもpipを使う際の注意点として挙げられているが、guestbookアプリケーションの開発に利用するFlaskのバージョンが上がって pip install -U Flask
でアップグレードし、もしFlaskの依存する他のPythonパッケージが変わった場合、不要になったパッケージの削除には追随できない。
この辺りを楽に管理するためにpip-toolsを使う。
$ pip install pip-tools
まずguestbookアプリケーション本体の動作に必要となる依存パッケージを requirements.in
ファイルに記述する。
Flask
次にguestbookアプリケーションの開発作業でのみ必要となる依存パッケージを dev-requirements.in
ファイルに記述する。このファイルでは requirements.in
の内容も取り込むようにする。
-r requirements.in
autopep8
docutils
wheel
pip-tools
pytest-pep8
それぞれのファイルをpip-toolsに付属する pip-compile
でコンパイルすると、最新の依存バージョンが書き出される。
# requirements.txtファイルの書き出し
$ pip-compile requirements.in
# dev-requirements.txtファイルの書き出し
$ pip-compile dev-requirements.in
# 依存バージョンに更新があった場合の手元virtualenv環境への反映
$ pip-sync dev-requirements.txt
Test PyPIへのパッケージ登録・公開方法はHow to submit a package to PyPIのページに詳しい。
.pypirc
ファイルを作成し pypi
と pypitest
の設定を記述するpassword:
は空欄でも登録時にプロンプトで入力できるため問題は無いguestbook
だと他と被る可能性が高いため {アカウント名}.guestbook
のような名前を使うと良い.pypirc
では pypitest
という名前でTest PyPI Serverが登録しておく。
[distutils]
index-servers=
pypi
pypitest
[pypitest]
repository = https://testpypi.python.org/pypi
username = raimon49
password =
[pypi]
repository = https://pypi.python.org/pypi
username = raimon49
password =
この状態だと -r pypi
オプションで、登録と公開する先をTest PyPIに指定できる。
# registerの時にパスワードを尋ねられてuploadでもそれが認証に使われる
$ python setup.py register -r pypitest sdist bdist_wheel upload -r pypitest
Pythonパッケージの作成方法について、『Pythonプロフェッショナルプログラミング 第2版』の内容を少しアレンジしてまとめた。本書はビープラウド社のノウハウが中心であるため、構成管理にMercurialを採用しているのかなと感じた。自分もMercurialは一時期プライベートで使っていたが、世間の流れが完全にGitへ傾いてしまったため、今回学んだ内容もGitで管理したかった。
まとまった量のREADMEドキュメントをreStructuredTextフォーマットで書いたのは今回が初めてで、Markdownとの違いも見えて良い経験になった。