bottle(python的一個小的伺服器框架)的原始碼閱讀(一)
bottle學習的不是很多,用bottle實現了一個連結mongodb的server。
索性bottle的原始碼也不是很多(4000行,主要的程式碼部分)。
所以我就去讀了一下原始碼:
from bottle import Bottle, run
from bottle.ext.mongo import MongoPlugin
app = Bottle()
plugin = MongoPlugin(uri='127.0.0.1', db='collection', json_mongo=True)
app.install(plugin)
@app.route('/', method='GET')
def main(mongodb):
return "hello, world"
# 正式啟動伺服器,在8080埠
run(app, host=SERVER_HOST , post=SERVER_PORT)
先簡化一下,分析上面幾行的過程
補丁的安裝
bottle支援各種補丁,可以自己定製一個,然後安裝進去就可以了。
app = Bottle()
plugin = MongoPlugin(uri='127.0.0.1', db='collection', json_mongo=True)
app.install(plugin)
主要是上面這幾行。
我們重點看一下install方法裡面的情況。
各種牛b的程式碼,主要是
self.plugins.append(plugin)
這一行,把補丁加入到Bottle這個類的補丁plugins集合中。
具體什麼時候執行的等下面再講。
@app.route
這是個明顯的decorator
對應Bottle類裡面的def route:方法:
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
return decorator(callback)
其實裡面的東西並不是很多
由於我們只是執行
@app.route(‘/’, method=’GET’)
所以,其實就是執行下面程式碼一次。
route = Route(self, rule, verb, callback, name=name,
plugins=plugins, skiplist=skiplist, **config)
self.add_route(route)
裡面的verb就是‘GET’, rule就是‘/’, callback就是被修飾的main函式。建立好route之後就會被 呼叫 add_route(route)
def add_route(self, route):
self.routes.append(route)
self.router.add(route.rule, route.method, route, name=route.name)
self.routes.append(route)這個沒什麼好說。
self.router.add東西有點多,當然因為我們就是一個靜態的路徑(非正則的路徑),所以其實就是
if is_static and not self.strict_order:
self.static.setdefault(method, {})
self.static[method][self.build(rule)] = (target, None)
return
如果是非靜態的話,會解析然後呼叫
def _compile(self, method)加入到
self.dyna_regexes[method]這個裡面。
run!!!
各種組裝好之後就開始run了。
run的話,額,大概就是指定個server(預設為裡面的WSGIRefServer),然後把app放在上面run,然後app會被呼叫call方法
然後呼叫wsgi方法
wsgi中有這麼一行
out = self._cast(self._handle(environ))
先呼叫_handle
_handle裡面
route, args = self.router.match(environ)
return route.call(**args)
呼叫 match裡面就會呼叫
靜態為例(裡面的路徑和方法我們在@app.route方法中已經填寫過了)
if method in self.static and path in self.static[method]:
target, getargs = self.static[method][path]
return target, getargs(path) if getargs else {}
動態的也類似,然後就是各種給引數,
然後route.call(**args)
執行一下。
這個call會呼叫_make_callback方法,此方法預先執行各個補丁的apply方法,比如我的那個MongoPlugin補丁,就會被執行,然後把預先為這個方法傳入引數比如傳入個mongodb的連線。
main(mongodb = concrete_mongodb)
通過@cached_property的封裝,保證每個方法包裝一次。
然後就可以愉快的呼叫call了得到結果了。
看不懂?直接上圖
下面是app.static 和app.dyna_regexas的來源
plugins來源:加入到app.plugins。然後在第一次被呼叫的時候會去看一下。然後安裝上。app.install是直接新增影響所有的函式,@route裡面如果寫了,那隻會影響單獨的函式。