Pythonの軽量WebフレームワークBottleのソースを読む ルータ編
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