Python Bottleのソースを読む 起動編


Pythonの軽量WebフレームワークBottleのソースを読む 起動編

Bottleとは

Bottle is a fast, simple and lightweight WSGI micro web-framework for Python. It is distributed as a single file module and has no dependencies other than the Python Standard Library.

Pythonの軽量Webフレームワークで、特徴はシンプルで早く、Pythonの標準ライブラリにも依存していないWebフレームワークであることとフレームワーク本体が1ファイルで構成されていることである

Class Hierarchy

20160204171644

20160204171658

Doxygenを使って出力した図

Bottleは1ファイルながら中でそれぞれのクラス、主にServer、Templateが継承関係にあるのがわかる(コード量は4000行位)。ServerやTemplateクラスはたくさんあるが実際はその中のどれかを選択して利用する形となる

起動

Bottleの起動はrun()を呼び出す方法とコマンドラインインターフェースを使う方法が用意されている

run()を使う方法

以下のように記載し起動することでサーバが立ち上がる

from bottle import run, route
@route('/')
def index():
return 'Hello World'
run(host='localhost', port=8000, debug=True)

コマンドラインインターフェースを使う場合

以下のコマンドで起動可能

# コントローラを指定
python -m bottle 'package.controller'
# 説明は省略するが明示的にアプリを指定することも可能
python -m bottle 'package.controller:app'

controller.py

# runは不要
from bottle import ,route
@route('/')
def index():
return 'Hello World'

起動処理を確認する

コマンドラインインターフェースを使う場合

mainが2箇所あるが、これはサーバアダプダに必要なライブラリを必要としているからで、1つ目のmainでまずサーバアダプタに必要なライブラリを読み込み2つ目のmainでサーバが起動される仕組みになっている

run()でサーバを起動する

run()では、渡された引数の値をそれぞれ読み込んだあと最後にServerAdapterのrun()を呼び出している。Bottleでは多くのサーバをServerAdapterを継承することでサポートしており、指定されたサーバを起動するようになっている。指定しない場合はwsgirefがデフォルトで呼ばれる。また、appパラメータは特別指定しなければ、内部で自動的にdefaultが使われるので特別指定する必要はない。

サーバをロードするまで処理を追ってみる

まず、Bottleでサポートしているサーバは以下のように宣言してある。

server_names = {
'cgi': CGIServer,
'flup': FlupFCGIServer,
'wsgiref': WSGIRefServer,
'waitress': WaitressServer,
'cherrypy': CherryPyServer,
'paste': PasteServer,
'fapws3': FapwsServer,
'tornado': TornadoServer,
'gae': AppEngineServer,
'twisted': TwistedServer,
'diesel': DieselServer,
'meinheld': MeinheldServer,
'gunicorn': GunicornServer,
'eventlet': EventletServer,
'gevent': GeventServer,
'geventSocketIO': GeventSocketIOServer,
'rocket': RocketServer,
'bjoern': BjoernServer,
'aiohttp': AiohttpServer,
'auto': AutoServer,
}

サーバを指定するには、run()実行時にserverとして必要な値を文字列で指定して渡す。以下に記載の通りデフォルトでは’wsgiref’が指定されている。

def run(app=None,
server='wsgiref',
host='127.0.0.1',
port=8080,
interval=1,
reloader=False,
quiet=False,
plugins=None,
debug=None,
config=None, **kargs):

このあたりでサーバを取得し設定しておりServerAdapterを継承していない値が渡された場合はサポート外としてExceptionを投げる

if server in server_names:
server = server_names.get(server)
if isinstance(server, basestring):
server = load(server)
if isinstance(server, type):
server = server(host=host, port=port, **kargs)
if not isinstance(server, ServerAdapter):
raise ValueError("Unknown or unsupported server: %r" % server)

WSGIRefServerなどは標準エラーを出力する設定になっている

server.quiet = server.quiet or quiet
if not server.quiet:
_stderr("Bottle v%s server starting up (using %s)...\n" %
(__version__, repr(server)))
_stderr("Listening on http://%s:%d/\n" %
(server.host, server.port))
_stderr("Hit Ctrl-C to quit.\n\n")

reloaderをTrueにした場合はFileCheckerThread(ファイルの変更をチェックして変更があったら自動でリロードする)を立ち上げて起動する

if reloader:
lockfile = os.environ.get('BOTTLE_LOCKFILE')
bgcheck = FileCheckerThread(lockfile, interval)
with bgcheck:
server.run(app)
if bgcheck.status == 'reload':
sys.exit(3)
else:
server.run(app)

簡単に起動までの流れをおってみたが、細かく見てみると他にも起動オプションがあるので必要に応じて設定するとよい
ざっと見てみたところBottleは1ファイルで構成されているので中身を確認しやすくPython初級者の私にとってはとっつきやすいフレームワークに思う。起動自体も1ファイルだけでできるので開発も簡単に始められる。ただその分逆にどのようにアプリケーションとしてファイルを構成していけばいいかが分かりにくいというデメリットもあるのではと感じた。最終的には自分なりに構成していけばいいのだがその辺はまた追って確認していこうと思う。


関連記事