摘要:在源碼閱讀一中,我們了解了如何接收請求,處理請求以及如何檢測模塊變化重啟。接下來我們看一下源碼是怎么實現的經過封裝后,最終獲得的是具備有一些屬性的裝飾器當為時,將的屬性傳遞給,使其具備相同的屬性。
在《Bottle源碼閱讀(一)》中,我們了解了bottle如何接收請求,處理請求以及如何檢測模塊變化重啟server。在ServerHandler類中的run函數中,application接受了兩個參數,一個envrion, 和一個start_response的方法,接下來我們就來了解我們寫的應用函數是如何被封裝成適配的application。
1.上層調用先看一個簡單的例子
from bottle import get, run @get("/hello") def hello(): return "hello" run(host="localhost", port=8080)
通過get裝飾器,使requestline ’/hello‘ 路由到hello函數,并將函數返回的結果通過WSGIRequestHandler 中的wfile返回。
接下來我們看一下get源碼是怎么實現的
route = make_default_app_wrapper("route") # 經過封裝后,get最終獲得的是具備有Bottle.get一些屬性的裝飾器 get = make_default_app_wrapper("get") post = make_default_app_wrapper("post") put = make_default_app_wrapper("put") delete = make_default_app_wrapper("delete") 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") def make_default_app_wrapper(name): """ Return a callable that relays calls to the current default app. """ # 當name為"get"時,將Bottle.get的屬性傳遞給wrapper,使其具備相同的屬性。 @functools.wraps(getattr(Bottle, name)) def wrapper(*a, **ka): # 使用默認的app, 也就是說,應用函數的裝飾器實際是Bottle.get(*a, **ka) return getattr(app(), name)(*a, **ka) return wrapper app = default_app = AppStack() app.push() class AppStack(list): """ A stack-like list. Calling it returns the head of the stack. """ def __call__(self): """ Return the current default application. """ return self[-1] def push(self, value=None): """ Add a new :class:`Bottle` instance to the stack """ if not isinstance(value, Bottle): value = Bottle() self.append(value) return value
我們可以順便看一下wraps和update_wrapper的源碼, wraps實際調用了partial函數(C寫的),update_wrapper保留了被裝飾函數原來的屬性("__module__", "__name__", "__doc__")
def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): for attr in assigned: setattr(wrapper, attr, getattr(wrapped, attr)) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Return the wrapper so this can be used as a decorator via partial() return wrapper def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)2.Bottle類中的應用函數裝飾器
由上述我們可以知道應用函數裝飾器@get(’/hello‘),實際為Bottle.get(a, *ka) 。get實際調用route,hello函數作為callback, ’/hello‘作為path, 并將Route實例后添加進self.route,當接收請求時再由self.match調用self.router.match(environ)返回對應的Route實例,并將callback執行結果響應返回
def get(self, path=None, method="GET", **options): """ Equals :meth:`route`. """ return self.route(path, method, **options) def route(self, path=None, method="GET", callback=None, name=None, apply=None, skip=None, **config): if callable(path): path, callback = None, path plugins = makelist(apply) skiplist = makelist(skip) def decorator(callback): # TODO: Documentation and tests 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) if callback else decorator3.斷點查看請求響應過程
1. ServerHandler類中的run函數中, application 為Bottle一個實例 self.result = application(self.environ, self.start_response) 2. bottle.Bottle類中 def __init__(self, catchall=True, autojson=True): self.router = Router() # Router類主要建立url,method和應用函數的映射字典,同時實現match方法 def add_route(self, route): """ Add a route object, but do not change the :data:`Route.app` attribute.""" self.routes.append(route) self.router.add(route.rule, route.method, route, name=route.name) if DEBUG: route.prepare() def __call__(self, environ, start_response): """ Each instance of :class:"Bottle" is a WSGI application. """ return self.wsgi(environ, start_response) def wsgi(self, environ, start_response): ... out = self._cast(self._handle(environ)) ... return out def _handle(self, environ): ... # 上述中應用函數的裝飾器,就將應用函數和路徑作為參數實例化了一個Route實例,并將它添加到了router中,調用match時,通過正則表達式匹配調用相應的route實例 route, args = self.router.match(environ) ... return route.call(**args) ... 3.bottle.Route類,主要是封裝了應用函數及其對應的metadata和配置 @cached_property def call(self): """ The route callback with all plugins applied. This property is created on demand and then cached to speed up subsequent requests.""" return self._make_callback() def _make_callback(self): callback = self.callback for plugin in self.all_plugins(): try: if hasattr(plugin, "apply"): api = getattr(plugin, "api", 1) context = self if api > 1 else self._context callback = plugin.apply(callback, context) ... return callback 4. 在上述的調用過程的plugin在本例中使用JSONPlugin, 將應用函數的結果序列化之后返回 class JSONPlugin(object): name = "json" api = 2 def __init__(self, json_dumps=json_dumps): self.json_dumps = json_dumps def apply(self, callback, route): dumps = self.json_dumps if not dumps: return callback def wrapper(*a, **ka): try: rv = callback(*a, **ka) except HTTPError: rv = _e() if isinstance(rv, dict): #Attempt to serialize, raises exception on failure json_response = dumps(rv) #Set content type only if serialization succesful response.content_type = "application/json" return json_response elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): rv.body = dumps(rv.body) rv.content_type = "application/json" return rv return wrapper4.緩存響應結果
上述代碼中,我們注意到在調用call函數時使用了cached_property的裝飾器,當第一次發起請求時,應用函數會被執行,而結果也將被保存。當再次請求,只需要從__dict__中獲取相應的屬性值即可
class cached_property(object): """ A property that is only computed once per instance and then replaces itself with an ordinary attribute. Deleting the attribute resets the property. """ def __init__(self, func): self.__doc__ = getattr(func, "__doc__") self.func = func def __get__(self, obj, cls): if obj is None: return self # 在調用時,會先從__dict__屬性中查找對應的屬性值,如果找不到則再使用func函數的執行結果 value = obj.__dict__[self.func.__name__] = self.func(obj) return value
看到這里,我們大概就清楚了從發起請求到解析請求,路由分發,返回應用函數結果的大概流程。url與callback的映射關系建立和match需要重點關注一下Router類,而callback的結果解析與相關的metadata則需要繼續關注Route類
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/38447.html
摘要:而其他的引擎,例如能夠幫我們進行驗證登錄自此,官網的我們已經大致有了了解后續我們可以選擇運用該框架實現一些簡單的應用,或者可以深入研究其源碼,提升自身的編程水平 在初識Bottle(一)中,我們了解了Bottle的基本用法在Bottle源碼閱讀(一)和Bottle源碼閱讀(二)可以查看個人對bottle源碼的相關閱讀筆記 下面繼續閱讀Bottle的官方文檔https://bottlep...
摘要:最近在閱讀微型框架的源碼,發現了中有一個既是裝飾器類又是描述符的有趣實現。所以第三版的代碼可以這樣寫第三版的代碼沒有使用裝飾器,而是使用了描述符這個技巧。更大的問題來自如何將描述符與裝飾器結合起來,因為是一個類而不是方法。 最近在閱讀Python微型Web框架Bottle的源碼,發現了Bottle中有一個既是裝飾器類又是描述符的有趣實現。剛好這兩個點是Python比較的難理解,又混合在...
摘要:在初識一中,我們了解了框架的基本用法。在本篇文章中,我們通過源碼來探究一些基本原理。因此下一步就是研究我們寫的應用函數是如何被封裝成適配的 在初識bottle(一)中,我們了解了bottle框架的基本用法。在本篇文章中,我們通過源碼來探究一些基本原理。 1. run的實現 所有的框架請求響應都基于一個原理http請求 --> wsgi服務器 --> wsgi接口(實際就是框架中自定義...
摘要:簡介官網上對它的定位是一個微開發框架。另外一個必須理解的概念是,簡單來說就是一套和框架應用之間的協議。功能比較豐富,支持解析自動防止攻擊繼承變量過濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創,原文發布在個人博客。 flask 簡介 Flask 官網上對它的定位是一個微 python web 開發框架。 Flask is a micro...
摘要:安裝是一個輕量型的不依賴于任何第三方庫的框架,整個框架只有一個文件。向打聲招呼吧新建一個文件在瀏覽器或者,,得到結果當使用裝飾器綁定路由時,實際是使用了的默認應用,即是的一個實例。 1. 安裝 bottle是一個輕量型的不依賴于任何第三方庫的web框架,整個框架只有bottle.py一個文件。 wget http://bottlepy.org/bottle.py 2. 向bottl...
閱讀 1883·2021-11-22 09:34
閱讀 3010·2021-09-28 09:35
閱讀 13374·2021-09-09 11:34
閱讀 3594·2019-08-29 16:25
閱讀 2820·2019-08-29 15:23
閱讀 2035·2019-08-28 17:55
閱讀 2424·2019-08-26 17:04
閱讀 3044·2019-08-26 12:21