Posted on

カンファレンスを最大限に楽しむ方法

Talk Python To Me という PodCast を聞いている中で興味深い話があった。

Talk Python To Me Podcast

Talk Python To Me is a podcast for developers who are passionate about Python. Learn about the language and related technologies.

Episode #161: Django 2.0 の 00:58:20〜のトーク内容で「カンファレンスを最大限に楽しむ方法は?」という質問に対する回答

まず第一に参加できるなら参加しましょう。もし US や北アメリカのカンファレンスに行けない場合は、ローカルで開催されているカンファレンスに参加しましょう。そしてもし参加したら友達とくつろぐのはやめて新しい友だちをつくりましょう。自分の視野を広げるために参加しているのだから。それが簡単でないことはわかります。でもそうすることで新しいアイデアやコネクションをつくることができて新しい視野がひろがります。そして、それができた場合でも自分の知識をみせびらかすようなことはやめて、そのかわりに質問をしましょう。そして私は、カンファレンスに参加してハイヤリングをした立場として分かりますが、知識があるひとよりもむしろ、新しいことを見つけ出して熱心に学ぼうとしている人を雇おうとします。
以前にカンファレンスに参加したことがある人もそうでない人も、カンファレンスのボランティアをやるのもよいです。ボランティアをする中で関係ができあがり、新しいコネクションができます。私はボランティアをすることで自分のキャリアの中で大きな変化を得ることができ、とても良い人で出会うきっかけになりました。恥ずかしいとか思うかもしれませんが、そんなことは誰も気にしてはいません。自分もとても恥ずかしいことをした経験があるけど誰も覚えてはいませんよ。
ソース:https://talkpython.fm/episodes/transcript/161/django-2.0

内容は意訳してる箇所や多少省略してる箇所があるので詳しく知りたい方はソースを見てください。この内容はトークする側ではなく単純に参加する側へ向けた話。
そしてある意味当たり前のような話ではある。ただ、恥ずかしがるとかそういった考えはなんとなく日本的もしくはアジア的なところがあるのかなと思っていたので、アメリカでもこういったことが話されてるんだなっていうところに興味が持てた内容でした。

話し手はこの方

Daniel Roy Greenfeld (@pydanny) | Twitter

The latest Tweets from Daniel Roy Greenfeld (@pydanny). Co-Author Two Scoops of Django (https://t.co/h98NETIALQ), open source coder, husband of @audreyr, former NASA coder. Los Angeles, CA

Posted on

「PyCon APAC 2018 in Singapore」参加レポートを寄稿した

PyCon APAC 2018 in Singaporeに参加してgihyo.jpさんにてレポートを寄稿した。
今年のレポートはPyCon JPのスタッフと元スタッフメンバーで書いた。カンファレンスのセッションレポートだけでなくカンファレンス参加に関連するコラムも盛り込んでいてめちゃくちゃ読み応えのある内容になったと思う。

Day1レポート

目次

  • Day1
    • PyCon APACとは
    • シンガポールと今年のカンファレンス会場
    • Keynote ~ Tell me your secrets – Privacy in machine learning systems
    • セッション
      • Build a Data-Driven Web App That Everyone Can Use
      • TeaTeaching Computers ABCs: A Quick Intro to Natural Language Processing
      • Better Care and Feeding of Machine Learning Model
    • コラム
      • APACで発表者をしてみて
      • PyCon APACミーティング
    • 終わりに

Day1:プライバシーを機械学習でどう守るか?/機械学習の基礎と応用

こんにちは, taisaです。2018年5月31日(木)~6月2日(土)にかけて行われた「 PyCon APAC 2018 in Singapore 」に参加してきました。本レポートでは最後の2日間に行われたカンファレンスデイの様子やセッション以外の現地滞在の様子などを2回に分けて,当日参加したPyCon JPスタッフや元スタッフのメンバーがお届けします。 PyCon …

Day2レポート

目次

  • Day2
    • keynote
    • セッション
      • Concurrency vs Parallelism
      • Elements of Programming Interviews in Python
      • Streaming data processing pipelines with Apache Beam in Python, naturally!
    • コラム
      • PyCon APACの開催国となったシンガポールについて
      • 海外カンファレンスに参加してみて
      • 2日目の夜について
    • 終わりに

Day2:今すぐ始められる機械学習,”Pythonic”なコードを書くには?

こんにちは, taisaです。第2回となる今回は,「⁠ PyCon APAC 2018 in Singapore 」のカンファレンスデイ2日目の様子をお届けします。⁠キーノート」や「セッション」のレポートの他にも「はじめて海外カンファレンスに参加してみて」や「夜の懇親会について」「⁠シンガポールについて」のコラムもありますので合わせてご覧ください。 (家治亮) 2日目のKeynoteには …

PyCon APACしか参加したことはないけど海外カンファレンスは本当に刺激的でおもしろい。来年が楽しみだし他にも行きたい。

Posted on

PyCon JP 2017にスタッフとして参加してきた

ようやく落ち着いてきたのでだいぶ今更ながらPyCon JP 2017の参加レポートを書く
PyCon JP 2017 in Tokyo | Sep 7th – Sep 10th


はじめに

まずはじめに、自分にとってのPyCon JPを簡単に振り返って見る。最初は2014年に一般参加者として参加したのがはじまりだった。そして翌年はじめてスタッフとしてジョインした。ただこの年は開催数日前にこどもが産まれ、当日の参加はできなかった。そしてその翌年の2016年は絶望的な業務の忙しさによりスタッフになることさえもできずに終わってしまった。そして今年、例年のような忙しさがありつつも無理やり時間を作ってスタッフとして準備・当日参加を果たすことができた。

スタッフとして

スタッフとしては今年、2015年と同様にプログラムチームというチームで活動をした。プログラムチームはプログラムといってもPythonを書くわけではなく、スピーカーを募集したり当日のスケジュールを組んだりとカンファレンスのコンテンツを決めていくような活動がメイン。その中で今年はキーノート担当として活動した。今年のキーノートは海外1名・国内1名だったが、それぞれのスピーカーとコミュニケーションができたのは非常に有意義だった。また、今年やりきれなかったこととしては、システムも少し触ろうとしていたけどキャパオーバーにつき並行で進めることは全くできなかった。

参加者として

今年はスタッフとして参加したので、セッションのチェアマンをしたりともろもろやることがあったので基本セッションを見ることはできず、未だ動画でもみることもできておらず、セッションはまだほとんど聞けてないけど「Keynote」「スポンサーブース」、昼休憩にやった「メディア会議」「ジョブフェア」、「Youth Coder」「1日目のParty」「2日目の関係者パーティ」とセッション以外のところだけでもめちゃめちゃ楽しむことができた。特に1日目のPartyは食事豪華で酒の種類も多く、いろんな人と話しすることができてめちゃめちゃ楽しめた。気づけば2014年以降一度も参加できていなかったPyCon JPだったけど、スタッフをはじめて苦節3年、ようやく最初から最後まで参加することができて本当によかった。

来年は

来年はどこまでコミットできるかわからないけどもちろんスタッフとして参加するし、場所はまだ決まっていないけどPyCon APACも必ず参加しようと思う。もはや気づけばこのカンファレンスが1年間のモチベーションの1つにもなっていたりしてコミュニティってすげーとひしひしと感じている次第。
2014年一般参加した時の写真
(この時一緒に参加した人も翌年からスタッフになっている)

当日の写真は以下にまとめて沢山あがっているので大変ありがたい
https://www.flickr.com/photos/pyconjp/albums/


主にPyConについての話をしたPyLadies TokyoでLTした時のスライド
PyConをきっかけに英語力を身につける

Posted on

PyCon APAC 2017 in マレーシア 参加レポ

PyCon APAC 2017に参加してきたのでその参加レポ。自分にとっては初めての海外カンファレンスだったのでとても有意義な旅行になった。

PyCon APACって?

毎年開催されているアジア太平洋のPythonカンファレンスで去年は韓国だったが、今年はマレーシアのクアラルンプールで開催された。

クアラルンプールへ

前日の朝に到着

とりあえずチェックインまで軽く街を散策

PyConAPACに参加している日本メンバーで食事会をした。マレーシアはご飯おいしいしツインタワーすごいしでとてもよいところだった。

PyCon APAC 2017 in マレーシア 前夜祭楽しかった。飯うまだしツインタワー凄いしいいところ。

たいささん(@taisa831)がシェアした投稿 – 2017 8月 25 8:02午後 PDT

カンファレンス初日

キーノート

スピーカーはLuis Miguel Sanchez

Tea Break

30分のTea Break。Tea Breakといいつつ食事も提供された。

Understanding The World With The Help Of Python

Data Mining of News (Intermediate)

ランチ

ランチは大部屋で

Using machine learning to try and predict taxi availability

Introduction to the data analysis using python(Beginner)

Introduction to Analytics studies in real business

The Python You Don’t Know

Velocity. Agility. Python

LT&クロージング

LT5本やってクロージング

慣れない英語のトークを一日ほぼノンストップで聞き続けたのでグッタリ。。。なので翌日は休み休み聞く事にした。

カンファレンス2日目

キーノート

2日目キーノートはJessica McKellar
プログラマーのマインドの話でききごたえがあった。

Tea Break

2日目も30分のTea Breakの間に食事が出た。慣れない食事で胃もたれ発生するも美味しいのでついつい沢山食べてしまった。

Silver Sponsors Panel Discussion

Beginner’s Guide to Scraping (Workshop style, Beginner)

ランチ

ランチは前日同様大部屋で提供されたが前日までの疲れがたまっていたのでホテルのプールサイドで休憩した。

Selenium Web Driver With Python (Intermediate)

Python’s situation in Japanese startups (Beginner)

LT

2日目のLTは8本くらいあった。日本から一緒に来た@c_bata_も緊急参戦して素晴らしいLTを披露していた。

最終日夜

参加メンバーで打ち上げ

まとめ

初海外カンファレンスだったこともあり、ためになったのと同時にかなり疲れたので次回からは(今回以上に)ゆるりと過ごしながら参加しようと思った。あとあらためて英語力のなさを痛感したのでどこかで頑張りたい。

PyCon APAC 2017の参加レポ

Posted on

FlaskをBottleと比較した雑感

以前Bottleのソースをチェックしてみた流れでFlaskも見てみた。結論から言うとBottleと大して変わらん。もちろんFlaskのほうがコード量が多く多少リッチではあるもののざっくり機能ベースで言うと大して変わらんのです。
そもそも両方ともマイクロWebフレームワークが売りなので当たり前といえば当たり前ですが、歴史的にもBottleが2009年リリースでFlaskが2010年4月1日(エイプリルフールのネタ
として)リリースと、名前もBottleに対抗してFlask(フラスコ)という名前をつけたということなので、もともとがBottleっぽいフレームワークを遊びで作ってみたという感じなんだと思います。それっぽいことはwikiに書かれています。

  • [Bottle Wiki](https://en.wikipedia.org/wiki/Bottle_(web_framework)
  • Flask Wiki

クラス構成に関しては以下にある通りで、Bottleが1ファイル構成でFlaskは機能にファイルが分かれてる構成という違いはあるものの、機能自体にほとんど違いはありません。ただFlaskはBlueprintという大規模アプリ対応の機能があります。
20160628103203
なお、使い方については公式ドキュメントとサンプルコードも複数用意されているのでアプリの構成や使い方が確認しやすいです。

Posted on

Python Bottleのソースを読む ユニットテスト編

Python Bottleのソースを読む テスト編
前回までで一通りメインの機能は確認できました。ではいよいよコード追加してプルリクを投げましょうというところですが、テストは?ということでユニットテストとカバレッジ状況をチェックしてみます。また自作アプリに対してのテスト方法もチェックしてみます。

Bottleのユニットテストとカバレッジ状況

GitHub上のBottleをクローンすると直下にtestディレクトリがあります。ここにずらーっとテストがありますが、よくよく見てみるとtestall.pyという全実行のエントリポイントが用意されているのでそれを実行します。

# noseを入れてない場合はインストール
pip install nose
# こんな感じに実行
nosetests --with-coverage --cover-html testall.py

でました。
20160623211937
bottle.pyだけを見てみると
76%
しっかりテストが書かれてるのがわかります。

bottle.py                       2277    485    849     79    76%

そしてhtmlをチェック
20160623211742
コードのカバレッジ状況が一目瞭然。あとは足りないとこテストしたり自分で処理追加してテストしたり。これでいろいろ安心。

BottleでつくったWebアプリのテスト方法

せっかくなのでWebアプリのテスト方法も見てみます。これは公式ドキュメントにしっかり書かれているのでそこをみればだいたい分かると思います。
noseインストール

pip install nose

テスト対象コード

import bottle
@bottle.route('/')
def index():
    return 'Hi!'
if __name__ == '__main__':
    bottle.run()

テストコード
単純にindex()を実行して結果をassertする

import mywebapp
def test_webapp_index():
    assert mywebapp.index() == 'Hi!'

テスト実行

nosetests test_app.py
Ran 1 test in 0.014s
OK

上記は直接関数をテストする方法ですがwebtestを使ってwebベースでアクセスしてテストする方法もあります。ただこの方法をやってみたところ明示的 app = Bottle()を宣言しないとうまく実行できませんでした。ここら辺はもう少しwebtestの使い方がわかればなんとかなるかもしれません。
webtestインストール

pip install webtest

テスト対象コード

from bottle import Bottle
# アプリを明示的に作成
app = Bottle()
@app.route('/')
def index():
    return 'Hi!'
if __name__ == '__main__':
    run(app=app)

テストコード
公式ドキュメントではログイン・ログアウトのテストが書かれてましたがここでは省略

from webtest import TestApp
import mywebapp
def test_index():
    app = TestApp(mywebapp.app)
    assert app.get('/').status == '200 OK'

テスト実行

nosetests test_app.py
Ran 2 tests in 0.003s
OK

これでいろいろ安心

Posted on

Python Bottleのソースを読む ルータ編

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

Class Hierarchy

ここの部分
20160208112338
 

Bottleのルータについて

bottleのルータは特に継承関係はなくRouteとRouterクラスで構成されている

Routerの役割

A Router is an ordered collection of route->target pairs. It is used to efficiently match WSGI requests against a number of routes and return the first target that satisfies the request. The target may be anything, usually a string, ID or callable object. A route consists of a path-rule and a HTTP method.
The path-rule is either a static path (e.g. `/contact`) or a dynamic path that contains wildcards (e.g. `/wiki/`). The wildcard syntax and details on the matching order are described in docs:`routing`.

RouterはRoute情報のコレクションを保持する。

Routeの役割

This class wraps a route callback along with route specific metadata and configuration and applies Plugins on demand. It is also responsible for turing an URL path rule into a regular expression usable by the Router.

Routeはrouteに対するcallback関数やroute特有の情報を保持するオブジェクト。RouteオブジェクトをRouterが一覧で保持することになる。

ルータの使い方

ルータは以下のようにデコレータを指定する。@getや@postはそれぞれGETのみPOSTのみを受け付ける

@route('/routes')
def routes():
    return 'routes'
@get('/get')
def get():
    return 'get'
@post('/post')
def post():
    return 'post'

デコレータには以下が用意されている

route     = make_default_app_wrapper('route')
get       = make_default_app_wrapper('get')
post      = make_default_app_wrapper('post')
put       = make_default_app_wrapper('put')
delete    = make_default_app_wrapper('delete')
patch     = make_default_app_wrapper('patch')
error     = make_default_app_wrapper('error')
mount     = make_default_app_wrapper('mount')
hook      = make_default_app_wrapper('hook')
install   = make_default_app_wrapper('install')
uninstall = make_default_app_wrapper('uninstall')
url       = make_default_app_wrapper('get_url')

ルータの処理

まず起動時のBottleインスタンス化時(default_appが生成されAppStackにpushされる)に、コンストラクタにてRouterがセットされる。

app = default_app = AppStack()
app.push()

コントローラのデコレータを読み込む

@get('/get')
def get():
    return 'get'

make_default_app_wrapperが呼ばれBottleのdef getが呼ばれる

def make_default_app_wrapper(name):
    """ Return a callable that relays calls to the current default app. """
    @functools.wraps(getattr(Bottle, name))
    def wrapper(*a, **ka):
        return getattr(app(), name)(*a, **ka)
    return wrapper

def getなどはrouteを少しラップしたもの

def get(self, path=None, method='GET', **options):
    """ Equals :meth:`route`. """
    return self.route(path, method, **options)
def post(self, path=None, method='POST', **options):
    """ Equals :meth:`route` with a ``POST`` method parameter. """
    return self.route(path, method, **options)
def put(self, path=None, method='PUT', **options):
    """ Equals :meth:`route` with a ``PUT`` method parameter. """
    return self.route(path, method, **options)
def delete(self, path=None, method='DELETE', **options):
    """ Equals :meth:`route` with a ``DELETE`` method parameter. """
    return self.route(path, method, **options)
def patch(self, path=None, method='PATCH', **options):
    """ Equals :meth:`route` with a ``PATCH`` method parameter. """
    return self.route(path, method, **options)

routeのdecoratorでRouteをインスタンス化(loadでcallback functionを取得しruleとセットで渡す)しBottleのroutesとRouterにrouteを追加する。これらを繰り返しrouteをすべて読み込む。これでpathから特定のactionを呼び出すことができるようになる。

def decorator(callback):
    if isinstance(callback, basestring): callback = load(callback)
    for rule in makelist(path) or yieldroutes(callback):
        for verb in makelist(method):
            verb = verb.upper()
            route = Route(self, rule, verb, callback,
                          name=name,
                          plugins=plugins,
                          skiplist=skiplist, **config)
            self.add_route(route)
    return callback
Posted on

Python Bottleのソースを読む プラグイン編

Pythonの軽量WebフレームワークBottleのソースを読む プラグイン編
Bottleを触ってみると通常のWebフレームワークには用意されているであろう機能がなかったりします。これはマイクロフレームワークであるが故であり、すべてがそろってない状態がむしろ正しい状態と言えます。Bottleではそういったものを補うためにプラグインが用意されていてある程度の機能はそちらでまかなうことができます。また、Plugin Development Guide を参考にしてプラグインを自作することも可能です。

Class Hierarchy

plugin用クラスはなくインターフェースが定義されているのでそれにしたがって実装します。

プラグインの使い方

公式ドキュメントに簡単な使い方が乗っているのでこちらを参考にすれば簡単に導入することができます。
以下がサンプルコードで、簡単な流れとしては
install()で任意のプラグインをインストールする
リクエスト時にプラグイン実行
となります。サンプルコードでは、プラグインでkwargsにdbをセットされている為ルートのアクションでdb変数が利用できるようになってます。

from bottle import route, install, template
from bottle_sqlite import SQLitePlugin
install(SQLitePlugin(dbfile='/tmp/test.db'))
@route('/show/<post_id:int>')
def show(db, post_id):
    c = db.execute('SELECT title, content FROM posts WHERE id = ?', (post_id,))
    row = c.fetchone()
    return template('show_post', title=row['title'], text=row['content'])
@route('/contact')
def contact_page():
    ''' This callback does not need a db connection. Because the 'db'
        keyword argument is missing, the sqlite plugin ignores this callback
        completely. '''
    return template('contact')

プラグインの作り方

プラグインの作り方も公式ドキュメントにもっともシンプルな形のサンプルがあります。これは実行速度をレスポンスヘッダーにつけて返す処理ですが、サーバ起動時にstopwatchをインストールし、リクエストが来た際にデコレータを実行(表現あってるか分からない)することでリクエストの処理時間は計測できるようになっています。

from bottle import response, install, route
import time
def stopwatch(callback):
    def wrapper(*args, **kwargs):
        start = time.time()
        body = callback(*args, **kwargs)
        end = time.time()
        response.headers['X-Exec-Time'] = str(end - start)
        return body
    return wrapper
install(stopwatch)
@route('/')
def index():
    return 'INDEX'

プラグインのinstall()処理をチェック

プラグインはインターフェースが定義されているのでそれに従って書く必要がありますが、setup()で事前準備をして、apply()で実際にプラグインの処理を実行するといった流れになります。実際にはstopwatch()のように関数だけを実装して渡すことも可能です。

def install(self, plugin):
    """ Add a plugin to the list of plugins and prepare it for being
        applied to all routes of this application. A plugin may be a simple
        decorator or an object that implements the :class:`Plugin` API.
    """
    if hasattr(plugin, 'setup'): plugin.setup(self)
    if not callable(plugin) and not hasattr(plugin, 'apply'):
        raise TypeError("Plugins must be callable or implement .apply()")
    self.plugins.append(plugin)
    self.reset()
    return plugin

プラグイン組み合わせて使うことでスムーズにBottleを使ったWeb開発が進められるようになります。ここまでで主要機能は一通りみることができたので、次回はテストをチェックしてみます。

Posted on

Python Bottleのソースを読む リクエスト・レスポンス編

Pythonの軽量WebフレームワークBottleのソースを読む リクエスト・レスポンス編
 

Class Hierarchy

ここの部分
20160213010717

リクエスト受付からレスポンスまで

前回、サーバの立ち上げ時にルーティングが読み込まれるところまでを確認したので今回はリクエスト受付からレスポンスを返すまでを見てみる。

まずリクエストが来るとwsgiref/handlers.pyのrunが呼ばれ、Bottleの__call__が呼び出される
wsgiref/handlers.py

self.setup_environ()
# リクエストを処理しレスポンスを取得する
self.result = application(self.environ, self.start_response)
# レスポンスを返す
self.finish_response()

bottle.py

def __call__(self, environ, start_response):
    """ Each instance of :class:'Bottle' is a WSGI application. """
    return self.wsgi(environ, start_response)

リクエストを処理する際に、LocalRequestのbindを呼び出しBaseRequestのコンストラクタにてenvironとLocalRequestをセットする。これでBaseRequestのラッパーからパラメータを取得可能になる。レスポンスも同様にLocalResponseのbindを呼び出す。

try:
    out = None
    environ['bottle.app'] = self
    request.bind(environ)
    response.bind()
    try:
        self.trigger_hook('before_request')
    except HTTPResponse:
        return  _e()
    # ここでリクエストの主処理を実行する
    out = _inner_handle()
    return out;
finally:
    if isinstance(out, HTTPResponse):
        out.apply(response)
    self.trigger_hook('after_request')

BaseRequestのコンストラクタ

self.environ = {} if environ is None else environ
self.environ['bottle.request'] = self

リクエスト情報は以下のように取得可能になる

def index():
    request.forms.get('test')
    return 'TOP'

LocalRequestは以下の通りマルチスレッドに対応している

#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a
#: request callback, this instance always refers to the *current* request
#: (even on a multi-threaded server).

hookポイントで以下のようにbefore_requestとafter_requestが利用可能

@hook('before_request')
def before_request():
    request.session = 'session'
@hook('after_request')
def after_request():
    print 'test'

_handleの_inner_handle()にてrouteのactionを呼び出しresponseを取得する

def _inner_handle():
    # Maybe pass variables as locals for better performance?
    try:
        route, args = self.router.match(environ)
        environ['route.handle'] = route
        environ['bottle.route'] = route
        environ['route.url_args'] = args
        return route.call(**args)

リダイレクトは以下のメソッドが用意されている

def redirect(url, code=None):
    """ Aborts execution and causes a 303 or 302 redirect, depending on
        the HTTP protocol version. """
    if not code:
        code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
    res = response.copy(cls=HTTPResponse)
    res.status = code
    res.body = ""
    res.set_header('Location', urljoin(request.url, url))
    raise res
Posted on

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

Doxygenを使って出力した図
20160204171644
20160204171658
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ファイルだけでできるので開発も簡単に始められる。ただその分逆にどのようにアプリケーションとしてファイルを構成していけばいいかが分かりにくいというデメリットもあるのではと感じた。最終的には自分なりに構成していけばいいのだがその辺はまた追って確認していこうと思う。