摘要:在源碼閱讀一中,我們了解了如何接收請求,處理請求以及如何檢測模塊變化重啟。接下來我們看一下源碼是怎么實(shí)現(xiàn)的經(jīng)過封裝后,最終獲得的是具備有一些屬性的裝飾器當(dāng)為時,將的屬性傳遞給,使其具備相同的屬性。
在《Bottle源碼閱讀(一)》中,我們了解了bottle如何接收請求,處理請求以及如何檢測模塊變化重啟server。在ServerHandler類中的run函數(shù)中,application接受了兩個參數(shù),一個envrion, 和一個start_response的方法,接下來我們就來了解我們寫的應(yīng)用函數(shù)是如何被封裝成適配的application。
1.上層調(diào)用先看一個簡單的例子
from bottle import get, run @get("/hello") def hello(): return "hello" run(host="localhost", port=8080)
通過get裝飾器,使requestline ’/hello‘ 路由到hello函數(shù),并將函數(shù)返回的結(jié)果通過WSGIRequestHandler 中的wfile返回。
接下來我們看一下get源碼是怎么實(shí)現(xiàn)的
route = make_default_app_wrapper("route") # 經(jīng)過封裝后,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. """ # 當(dāng)name為"get"時,將Bottle.get的屬性傳遞給wrapper,使其具備相同的屬性。 @functools.wraps(getattr(Bottle, name)) def wrapper(*a, **ka): # 使用默認(rèn)的app, 也就是說,應(yīng)用函數(shù)的裝飾器實(shí)際是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實(shí)際調(diào)用了partial函數(shù)(C寫的),update_wrapper保留了被裝飾函數(shù)原來的屬性("__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類中的應(yīng)用函數(shù)裝飾器
由上述我們可以知道應(yīng)用函數(shù)裝飾器@get(’/hello‘),實(shí)際為Bottle.get(a, *ka) 。get實(shí)際調(diào)用route,hello函數(shù)作為callback, ’/hello‘作為path, 并將Route實(shí)例后添加進(jìn)self.route,當(dāng)接收請求時再由self.match調(diào)用self.router.match(environ)返回對應(yīng)的Route實(shí)例,并將callback執(zhí)行結(jié)果響應(yīng)返回
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.斷點(diǎn)查看請求響應(yīng)過程
1. ServerHandler類中的run函數(shù)中, application 為Bottle一個實(shí)例 self.result = application(self.environ, self.start_response) 2. bottle.Bottle類中 def __init__(self, catchall=True, autojson=True): self.router = Router() # Router類主要建立url,method和應(yīng)用函數(shù)的映射字典,同時實(shí)現(xiàn)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): ... # 上述中應(yīng)用函數(shù)的裝飾器,就將應(yīng)用函數(shù)和路徑作為參數(shù)實(shí)例化了一個Route實(shí)例,并將它添加到了router中,調(diào)用match時,通過正則表達(dá)式匹配調(diào)用相應(yīng)的route實(shí)例 route, args = self.router.match(environ) ... return route.call(**args) ... 3.bottle.Route類,主要是封裝了應(yīng)用函數(shù)及其對應(yīng)的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. 在上述的調(diào)用過程的plugin在本例中使用JSONPlugin, 將應(yīng)用函數(shù)的結(jié)果序列化之后返回 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.緩存響應(yīng)結(jié)果
上述代碼中,我們注意到在調(diào)用call函數(shù)時使用了cached_property的裝飾器,當(dāng)?shù)谝淮伟l(fā)起請求時,應(yīng)用函數(shù)會被執(zhí)行,而結(jié)果也將被保存。當(dāng)再次請求,只需要從__dict__中獲取相應(yīng)的屬性值即可
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 # 在調(diào)用時,會先從__dict__屬性中查找對應(yīng)的屬性值,如果找不到則再使用func函數(shù)的執(zhí)行結(jié)果 value = obj.__dict__[self.func.__name__] = self.func(obj) return value
看到這里,我們大概就清楚了從發(fā)起請求到解析請求,路由分發(fā),返回應(yīng)用函數(shù)結(jié)果的大概流程。url與callback的映射關(guān)系建立和match需要重點(diǎn)關(guān)注一下Router類,而callback的結(jié)果解析與相關(guān)的metadata則需要繼續(xù)關(guān)注Route類
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38447.html
摘要:而其他的引擎,例如能夠幫我們進(jìn)行驗(yàn)證登錄自此,官網(wǎng)的我們已經(jīng)大致有了了解后續(xù)我們可以選擇運(yùn)用該框架實(shí)現(xiàn)一些簡單的應(yīng)用,或者可以深入研究其源碼,提升自身的編程水平 在初識Bottle(一)中,我們了解了Bottle的基本用法在Bottle源碼閱讀(一)和Bottle源碼閱讀(二)可以查看個人對bottle源碼的相關(guān)閱讀筆記 下面繼續(xù)閱讀Bottle的官方文檔https://bottlep...
摘要:最近在閱讀微型框架的源碼,發(fā)現(xiàn)了中有一個既是裝飾器類又是描述符的有趣實(shí)現(xiàn)。所以第三版的代碼可以這樣寫第三版的代碼沒有使用裝飾器,而是使用了描述符這個技巧。更大的問題來自如何將描述符與裝飾器結(jié)合起來,因?yàn)槭且粋€類而不是方法。 最近在閱讀Python微型Web框架Bottle的源碼,發(fā)現(xiàn)了Bottle中有一個既是裝飾器類又是描述符的有趣實(shí)現(xiàn)。剛好這兩個點(diǎn)是Python比較的難理解,又混合在...
摘要:在初識一中,我們了解了框架的基本用法。在本篇文章中,我們通過源碼來探究一些基本原理。因此下一步就是研究我們寫的應(yīng)用函數(shù)是如何被封裝成適配的 在初識bottle(一)中,我們了解了bottle框架的基本用法。在本篇文章中,我們通過源碼來探究一些基本原理。 1. run的實(shí)現(xiàn) 所有的框架請求響應(yīng)都基于一個原理http請求 --> wsgi服務(wù)器 --> wsgi接口(實(shí)際就是框架中自定義...
摘要:簡介官網(wǎng)上對它的定位是一個微開發(fā)框架。另外一個必須理解的概念是,簡單來說就是一套和框架應(yīng)用之間的協(xié)議。功能比較豐富,支持解析自動防止攻擊繼承變量過濾器流程邏輯支持代碼邏輯集成等等。那么,從下一篇文章,我們就正式開始源碼之旅了 文章屬于作者原創(chuàng),原文發(fā)布在個人博客。 flask 簡介 Flask 官網(wǎng)上對它的定位是一個微 python web 開發(fā)框架。 Flask is a micro...
摘要:安裝是一個輕量型的不依賴于任何第三方庫的框架,整個框架只有一個文件。向打聲招呼吧新建一個文件在瀏覽器或者,,得到結(jié)果當(dāng)使用裝飾器綁定路由時,實(shí)際是使用了的默認(rèn)應(yīng)用,即是的一個實(shí)例。 1. 安裝 bottle是一個輕量型的不依賴于任何第三方庫的web框架,整個框架只有bottle.py一個文件。 wget http://bottlepy.org/bottle.py 2. 向bottl...
閱讀 1904·2021-11-22 09:34
閱讀 3042·2021-09-28 09:35
閱讀 13499·2021-09-09 11:34
閱讀 3608·2019-08-29 16:25
閱讀 2840·2019-08-29 15:23
閱讀 2051·2019-08-28 17:55
閱讀 2440·2019-08-26 17:04
閱讀 3056·2019-08-26 12:21