webpy源碼分析(一):application的__init__()

本文通過(guò)分析webpy代碼學(xué)習(xí)python編程技巧涵亏、系統(tǒng)設(shè)計(jì)以及web編程的基本方法
我是邊看代碼代碼邊寫此文的镊屎,所以代碼中的有些問(wèn)題可能會(huì)放在后序的文章中詳細(xì)闡述惹挟。

webpy簡(jiǎn)單介紹

webpy是個(gè)使用python編寫的輕量級(jí)web服務(wù)器

相關(guān)資料

webpy源碼
webpy官網(wǎng)

測(cè)試用代碼

# test_webpy.py
import web  #指向源碼中的web目錄
        
urls = (
    '/(.*)', 'hello'  
)
app = web.application(urls, globals()) # 初始化

class hello:        
    def GET(self, name):
        if not name: 
            name = 'World'
        return 'Hello, ' + name + '!'

if __name__ == "__main__":
    app.run()           #啟動(dòng)

運(yùn)行結(jié)果

服務(wù)端:


image.png-40.3kB
image.png-40.3kB

瀏覽器:


image.png-19.6kB
image.png-19.6kB

application類基本功能分析

在繼續(xù)之前請(qǐng)了解wsgi相關(guān)概念,可以參考我的這篇文章缝驳。

1. 初始化步驟

即test_webpy.py第9行

app = web.application(urls, globals()) 
1. 參數(shù)urls用于存放用戶請(qǐng)求與處理邏輯的關(guān)系(此處是hello類)

對(duì)于是否是iter的判斷在py3helpers.py中有更完善的寫法连锯。


123.png
2. web.config
  • 框架對(duì)web.config進(jìn)行了初始化。我在application初始化以前將其內(nèi)容print了出來(lái)用狱,并將其初始化的地方分別列了出來(lái)运怖。其中_is_dev_mode函數(shù)實(shí)現(xiàn)中有說(shuō)到一個(gè)sys不存在argv成員的問(wèn)題,有興趣的同學(xué)可以看下注釋中的連接夏伊。
  • 其中debug配置項(xiàng)的具體功能有個(gè)描述摇展,也體現(xiàn)了框架的一些功能:“ when True, enables reloading, disabled template caching and sets internal error to debug error”。我對(duì)reloading和template caching的實(shí)現(xiàn)還是比較感興趣的溺忧,后續(xù)會(huì)詳細(xì)研究下實(shí)現(xiàn)方法咏连。
  • Storage擴(kuò)展了dict,增加了通過(guò)"."訪問(wèn)鍵值的功能鲁森。
123.png
3. handle_with_processors()與self.processor[]

其作用為在響應(yīng)每個(gè)http請(qǐng)求前后做一些額外處理祟滴。此處先不關(guān)注processor具體是什么,只關(guān)注這些processor是如何注入的歌溉,你可以把這種方式作為一種設(shè)計(jì)模式來(lái)理解垄懂。(作者認(rèn)為所有設(shè)計(jì)模式的目的只有一個(gè):解耦)。

我在每個(gè)processor以及http請(qǐng)求處理函數(shù)中增加了一條日志,額外增加了一個(gè)unload test的processor埠偿,后面會(huì)提供代碼透罢。先來(lái)看看執(zhí)行結(jié)果,注意執(zhí)行順序:


123.png

再來(lái)看看代碼:


123.png

圖注:

  1. 初始化
  • self.processors[]用于存放所有的processor冠蒋,其初始化與5個(gè)processor的添加都在__init__()中進(jìn)行羽圃,其中我新加的那個(gè)已經(jīng)在圖中指出。
  • loadhook()和unloadhook()為兩個(gè)輔助函數(shù)抖剿,分別用于在處理Http前后增加processor朽寞。
  • 注意代碼執(zhí)行順序與之前的日志輸出順序做比較,尤其是新加的那個(gè)unload test的執(zhí)行順序斩郎。
  1. 為其中一個(gè)load processor的內(nèi)容脑融,參考用

  2. loadhook()和unloadhook()
    這兩個(gè)輔助函數(shù)用于注冊(cè)processor用。

  • 其中processor函數(shù)的參數(shù)handler請(qǐng)理解為“下一步處理”或“下一步要做的事情”
  • processor函數(shù)中的h即為實(shí)際的processor缩宜,而此處的processor函數(shù)則是對(duì)實(shí)際函數(shù)的包裝
  • unloadhook與loadhook主要的不同是只有unloadhook需要負(fù)責(zé)“傳遞”結(jié)果肘迎,因?yàn)橛闷渥?cè)的processor是在http請(qǐng)求完成處理以后執(zhí)行的
  • 如果http請(qǐng)求處理返回的結(jié)果形式為generator,則需要對(duì)其進(jìn)行wrap(注意其中的wrap函數(shù))锻煌,以確保所有結(jié)果都遍歷完畢以后才執(zhí)行processor

4.實(shí)際執(zhí)行processor的地方

  • 方塊里面是調(diào)用processor的代碼妓布。之前說(shuō)過(guò),每個(gè)processor函數(shù)接收的handler參數(shù)表示“下一步處理”宋梧,所以這里不能傳processors(表示“剩余任務(wù)”)或者process(processors)(表示“剩余任務(wù)執(zhí)行結(jié)果”)
  • 倒數(shù)第二行的那個(gè)注釋所指的是unloadhook添加與執(zhí)行的順序是相反的匣沼,具體請(qǐng)看我添加的那個(gè)unload test的代碼執(zhí)行與日志輸出順序與self._unload的進(jìn)行比較

此設(shè)計(jì)模式總結(jié):

  1. 適用性
  • 要向外部提供接口,此處為向下層提供的handle_with_processors回調(diào)接口
  • 任務(wù)有明確的第三方處理者捂龄,此處已封裝為self.handle函數(shù)
  • 要向兩者之間增加額外的處理邏輯释涛,即把自己作為第三方處理者的代理,對(duì)接口參數(shù)或處理結(jié)果透?jìng)骰蛘咴偌庸ぃū纠又胁⑽丛偌庸?shù)或結(jié)果倦沧,但是很容易實(shí)現(xiàn))
  • 代理邏輯可以無(wú)限拓展

2.偽代碼
基礎(chǔ)代碼:

def 外部接口(請(qǐng)求參數(shù)):
    return 第三方處理函數(shù)(請(qǐng)求參數(shù))  

應(yīng)用該設(shè)計(jì)模式以后:

def 外部接口(請(qǐng)求參數(shù)):
    預(yù)處理邏輯一(請(qǐng)求參數(shù))
    預(yù)處理邏輯二(請(qǐng)求參數(shù))
     ...
    結(jié)果 = 第三方處理函數(shù)(請(qǐng)求參數(shù))  

    結(jié)果 = 再處理邏輯一(請(qǐng)求參數(shù)唇撬,上一步結(jié)果)
    結(jié)果 = 再處理邏輯二(請(qǐng)求參數(shù),上一步結(jié)果)
     ...
    return 結(jié)果
3. mapping和fvars

此為__init()__的第二展融、第三個(gè)參數(shù)局荚,其與webpy框架的使用方法有很大的關(guān)系。
先來(lái)修改下測(cè)試代碼:

import web

class hello:
    def __init__(self):
        self.str = 'I am %s\n' % self.__class__.__name__

    def GET(self, *args):
        self.str += 'args len is %d(%s)' % (len(args), args)
        return self.str

class hello2(hello):
    pass

urls = (
        '/hello', r'hello',
        '/hello_cls', hello,
        '/hello_arg/(\w*)', r'hello',
        '/hello_arg/(\w*)/(\w*)', r'hello',
        '/\w*', r'hello2',

        '/1/(\w*)', r'\1',
        '/2/(\w*)/(\w*)', r'\1',
        '/3/(\w*)/(\w*)/(\w*)', r'\1\2',

        '/4/hello_redi', r'redirect /2/hello/redi',
        )
app = web.application(urls, globals())

if __name__ == "__main__":
    app.run()

定義兩個(gè)類hello和hello2(繼承自hello)愈污,__init__的邏輯用來(lái)區(qū)分我是hello還是hello2耀态,GET函數(shù)用來(lái)打印傳進(jìn)來(lái)的參數(shù)。
在urls中定義了更多的條目用來(lái)進(jìn)行測(cè)試暂雹,其中條目是兩兩成對(duì)的首装,前者表示url請(qǐng)求,后者表示對(duì)應(yīng)的處理方式杭跪。webpy會(huì)按順序查找urls中的條目仙逻,如果找到則停止查找并進(jìn)行處理驰吓。
接下來(lái)我們對(duì)每一對(duì)條目進(jìn)行解釋:

  • '/hello', r'hello',


    image.png

    精確匹配,無(wú)任何參數(shù)傳遞

  • '/hello_cls', hello,
    精確匹配系奉,可以直接填寫處理的類檬贰,即hello

  • '/hello_arg/(\w*)', r'hello',


    image.png

/hello_arg/用來(lái)對(duì)應(yīng)到hello類,而括號(hào)中的內(nèi)容將作為參數(shù)傳遞給處理邏輯

  • '/hello_arg/(\w)/(\w)', r'hello',
    image.png

多個(gè)參數(shù)的情況缺亮,注意querystring不會(huì)作為參數(shù)傳遞

  • '/\w*', r'hello2',


    image.png

    其他情況翁涤,由hello2處理

  • '/1/(\w*)', r'\1',


    image.png
image.png

根據(jù)訪問(wèn)請(qǐng)求指定處理的類名,并且會(huì)作為參數(shù)傳遞

  • '/2/(\w)/(\w)', r'\1',

    image.png

    帶額外參數(shù)的情況

  • '/3/(\w)/(\w)/(\w*)', r'\1\2',

    image.png

    類名由多個(gè)參數(shù)組裝而來(lái)

  • '/4/hello_redi', r'redirect /2/hello/redi',

    image.png

    重定向萌踱,相當(dāng)于訪問(wèn)了http://localhost/2/hello/redi葵礼,訪問(wèn)后url會(huì)有變化

接下來(lái)我們看看webpy代碼的實(shí)現(xiàn):

123.png

圖注:

  1. 在初始化時(shí)傳給application的第二個(gè)參數(shù)主要用于符號(hào)的查找。具體的使用看箭頭的指向并鸵,fvars儲(chǔ)存的就是globals()鸳粉,從中可以找到hello類等符號(hào)
  2. mapping存放的就是對(duì)照表,也就是urls中的內(nèi)容园担。在初始化的時(shí)候?qū)⑵溥M(jìn)行了兩兩分組
  3. 在之前講到processor的時(shí)候届谈,我們有提過(guò)handle(),它負(fù)責(zé)處理http請(qǐng)求弯汰。其中self._match()用于查找mapping疼约,獲得具體處理的對(duì)象(fn),以及URI相關(guān)信息(args)蝙泼;self._delegate()則負(fù)責(zé)執(zhí)行處理邏輯
  4. re_subm的實(shí)現(xiàn)可以看一下其__doc__中的舉例
  5. cls = fvars[f]這行代碼在實(shí)際運(yùn)行過(guò)程中可能會(huì)有問(wèn)題,當(dāng)cls不存在時(shí)后端會(huì)直接拋出異常在瀏覽器中看到500的錯(cuò)誤
4. reload mod功能

__init__()最后一個(gè)參數(shù)表示是否開啟reload的功能劝枣。reload功能指無(wú)需重啟后端服務(wù)就可以修改http處理邏輯并使之生效汤踏。包括對(duì)照表urls和對(duì)應(yīng)的處理邏輯(比如hello類)

123.png

圖注:

  1. 通過(guò)注冊(cè)兩個(gè)processor,在每次響應(yīng)http請(qǐng)求前進(jìn)行mod是否更新的檢查(Reloader)以及mapping與fvars的更新(reload_mapping)舔腾。注意, main模塊是不能reload的溪胶,所以在__init__中將其作為一個(gè)非main模塊重新import。相關(guān)的代碼有一些問(wèn)題稳诚,我做了一些調(diào)整哗脖,見圖
  2. Reloader()檢查所有mod的時(shí)間戳是否有變化,并reload有變化的mod扳还。但是已經(jīng)被引用的mod除非重新賦值引用其的變量才避,否則無(wú)法更新。個(gè)人認(rèn)為從性能考慮氨距,只用檢查application對(duì)應(yīng)的mod桑逝,其他的可以忽略。如果使用python3執(zhí)行代碼俏让,需要將765的代碼改成:
except Exception as e:

下一篇

webpy源碼分析(二): application的run()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末楞遏,一起剝皮案震驚了整個(gè)濱河市茬暇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寡喝,老刑警劉巖糙俗,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異预鬓,居然都是意外死亡巧骚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門珊皿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)网缝,“玉大人,你說(shuō)我怎么就攤上這事蟋定》垭” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵驶兜,是天一觀的道長(zhǎng)扼仲。 經(jīng)常有香客問(wèn)我,道長(zhǎng)抄淑,這世上最難降的妖魔是什么屠凶? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮肆资,結(jié)果婚禮上矗愧,老公的妹妹穿的比我還像新娘。我一直安慰自己郑原,他們只是感情好唉韭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著犯犁,像睡著了一般属愤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酸役,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天住诸,我揣著相機(jī)與錄音,去河邊找鬼涣澡。 笑死贱呐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的入桂。 我是一名探鬼主播吼句,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼事格!你這毒婦竟也來(lái)了惕艳?” 一聲冷哼從身側(cè)響起搞隐,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎远搪,沒想到半個(gè)月后劣纲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谁鳍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年癞季,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倘潜。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绷柒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涮因,到底是詐尸還是另有隱情废睦,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布养泡,位于F島的核電站嗜湃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏澜掩。R本人自食惡果不足惜购披,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肩榕。 院中可真熱鬧刚陡,春花似錦、人聲如沸株汉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)郎逃。三九已至,卻和暖如春挺份,著一層夾襖步出監(jiān)牢的瞬間褒翰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工匀泊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留优训,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓各聘,卻偏偏與公主長(zhǎng)得像揣非,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躲因,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程早敬,因...
    小菜c閱讀 6,402評(píng)論 0 17
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語(yǔ)閱讀 3,660評(píng)論 0 7
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理忌傻,服務(wù)發(fā)現(xiàn),斷路器搞监,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • # 一水孩、框架概述 # 課程概述 1. laravel 4天(之前TP框架還是很大的區(qū)別)(國(guó)外框架) 2. 在線教...
    關(guān)進(jìn)一閱讀 371評(píng)論 0 0
  • 第十八章:浴火籠中鳥(下) 這屋里頭,鬧得不可開支琐驴。老太太在屋外頭俘种,聽出幾分惆悵與無(wú)奈。 原先秦氏和莊瑚是沒過(guò)來(lái)的...
    關(guān)塘閱讀 462評(píng)論 0 2