如今咱們將要向詩歌下載客戶端添加一些新的處理邏輯,包括在第九部分提到要添加的功能。不過,首先我要說明一點:我並不知道如何實現Byronification引擎。那超出了個人編程能力範圍。取而代之的,我想實現一個簡單的功能,即Cummingsifier。其只是將詩歌內容轉換成小寫字母:python
def cummingsify(poem) return poem.lower()
這個方法如此之簡單以致於它永遠不會出錯。版本5.0的實現代碼在twisted-client-5/get-poetry.py文件中。咱們使用了修改後的 cummingsify,其會隨機地選擇如下行爲:react
1.返回詩歌的小寫版本git
2.拋出一個GibberishError異常github
3.拋出一個ValueError算法
這樣,咱們便模擬出來一個會由於各類意料不到的問題而執行失敗的複雜算法。其它部分的僅有的改變在方法poetry_main中:編程
def poetry_main(): addresses = parse_args() from twisted.internet import reactor poems = [] errors = [] def try_to_cummingsify(poem): try: return cummingsify(poem) except GibberishError: raise except: print 'Cummingsify failed!' return poem def got_poem(poem): print poem poems.append(poem) def poem_failed(err): print >>sys.stderr, 'The poem download failed.' errors.append(err) def poem_done(_): if len(poems) + len(errors) == len(addresses): reactor.stop() for address in addresses: host, port = address d = get_poetry(host, port) d.addCallback(try_to_cummingsify) d.addCallbacks(got_poem, poem_failed) d.addBoth(poem_done) reactor.run()
所以,當從服務器上下載一首詩歌時,可能會出現以下狀況:安全
1.打印詩歌的小寫版本服務器
2.打印 」Cummingsify failed「並附上原始形式的詩歌app
3.打印」The peom download failed」。函數
爲了實現下面內容的效果,你能夠打開多個服務器或開一個服務器而打開此程序次,直到你觀察到全部不一樣的結果,固然也嘗試一下去鏈接一個沒有服務器值守的端口。
圖19是咱們給deferred添加回調後造成的callback/errback鏈:
圖19 deferred中的回調鏈
注意到,"pass-throug」errback經過addCallback添加到鏈中。它會將任何其接收到的Failure傳遞給下一個errback(即poem_failed函數)。所以poem_failed函數能夠處理來自get_poetry與try_to_commingsify二者的failure。下面讓咱們來分析下deferred可能會出現的激活狀況,圖20說明了咱們可以下載到詩歌而且try_to_commingsify成功執行的路線圖:
圖20 成功下載到詩歌而且成功變換其格式
在這種狀況中,沒有回調執行失敗,所以控制權一直在callback中流動。注意到poem_done收到的結果是None,這是由於它並無返回任何值。若是咱們想讓後續的回調都能觸及到詩歌內容,只要顯式地讓got_poem返回詩歌便可。
圖21說明了咱們在成功下載到詩歌后,但在try_to_cummingsify中拋出了GibberishError:
圖21 成功下載到詩歌但出現了GibberishError
因爲try_to_cummingsify回調拋出了GibberishError,因此控制權轉移到了errback鏈,即poem_fail回調被調用並傳入的捕獲的異常做爲其參數。
因爲poem_failed並無拋出獲異常或返回一個Failure,所以在它執行完後,控制權又回到了callback鏈中。若是咱們想讓poem_fail徹底處理好傳進來的錯誤,那麼返回一個None是再好不過的作法了。相反,若是咱們只想讓poem_failed採起一部分行動,但繼續傳遞這個錯誤,那麼咱們須要改寫poem_failed,即將參數err做爲返回值返回。如此一來,控制權交給了下一個errback回調。
注意到,迄今爲止,got_poem與poem_failed都不可能出現執行失敗的狀況,所以errback鏈上的poem_done是不可能被激活的。但在任何狀況下這樣作都是安全的,這體現了「防護式」編程的思想。好比在got_poem或poem_failed出現了bugs,那麼這樣作就不會讓這個bugs的影響進入Twisted的核心代碼區。鑑於上面的描述,能夠看出addBoth類型於try/except中的finally語句。
下面咱們再來看看第三種可能狀況,即成功下載到詩歌但try_to_cummingsify拋出了VauleError,如圖22:
圖22:成功下載到詩歌當cummingsify執行失敗
除了got_poem獲得是原始式樣的詩歌而不是小寫版的外,與圖20描述的狀況徹底相同。固然,控制權仍是在try_to_cummingsif中進行了轉移,即便用了try/except捕獲了ValueError並返回了原始式樣的詩歌。而這一切deferred並不知曉。
最後,咱們來看看當試圖鏈接一個無服務器值守的端口會出現什麼狀況,如圖23所示:
圖23 鏈接服務器失敗
因爲poem_failed返回了一個None,所以控權又回到了callback鏈中。
版本5.1
在版本5.0中咱們使用普通的try/except來捕獲try_to_cummingsify中的異常,而沒有讓deferred來捕獲這個異常。這其實並無什麼錯誤,但下面咱們將採起一種新的方式來處理異常。
設想一下,咱們讓deferred來捕獲 GibberishError
與ValueError
異常,並將其傳遞到errback鏈中進行處理。若是要保留原有的行爲,那麼須要下面的errback來判斷錯誤類型是否爲Valuerror,若是是,那麼返回原始式樣的詩歌,這樣一來,控制權再次回到callback鏈中並將原始式樣的詩歌打印出來。
但有一個問題:errback並不會獲得原始詩歌內容 。它只會獲得由cummingsify拋出的vauleError異常。爲了讓errback處理這個錯誤,咱們須要從新設計它來接收到原始式樣的詩歌。
一種方法是改變cummingsify以讓異常信息中包含原始式樣的詩歌。這也正是咱們在5.1版本中作的,其代碼實如今twisted-client-5/get-poetry-1.py中。咱們改寫ValueError異常爲CannotCummingsify異常,其能將詩歌做爲其第一個參數來傳遞。
若是cummingsify中外部模塊中一個真實存在的函數,那麼其最好是經過另外一個函數來捕獲非GibberishError並拋出一個CannotCummingsify異常。這樣,咱們的poetry_main就成爲:
def poetry_main(): addresses = parse_args() from twisted.internet import reactor poems = [] errors = [] def cummingsify_failed(err): if err.check(CannotCummingsify): print 'Cummingsify failed!' return err.value.args[0] return err def got_poem(poem): print poem poems.append(poem) def poem_failed(err): print >>sys.stderr, 'The poem download failed.' errors.append(err) def poem_done(_): if len(poems) + len(errors) == len(addresses): reactor.stop() for address in addresses: host, port = address d = get_poetry(host, port) d.addCallback(cummingsify) d.addErrback(cummingsify_failed) d.addCallbacks(got_poem, poem_failed) d.addBoth(poem_done)
而新的deferred結構如圖24所示:
圖24:版本5.1的deferrd調用鏈結構
來看看cummingsify_failed的errback回調:
def cummingsify_failed(err): if err.check(CannotCummingsify): print 'Cummingsify failed!' return err.value.args[0] return err
咱們使用了Failure中的check方法來確認嵌入在Failure中的異常是不是CannotCummingsify的實例。若是是,咱們返回異常的第一個參數(即原始式樣詩歌)。所以,這樣一來返回值就不是一個Failure了,控制權也就又回到callback鏈中了。不然(即異常不是CannotCummingsify的實例),咱們返回一個Failure,即將錯誤傳遞到下一個errback中。
圖25說明了當咱們捕獲一個CannotCummingsify時的調用過程:
圖25:捕獲一個CannotCummingsify異常
所以,當咱們使用deferrd時,能夠選擇使用try/except來捕獲異常,也可讓deferred來將異常傳遞到errback回調鏈中進行處理。
總結:
在這個部分,咱們加強了客戶端的Deferred的功能,實現了異常與結果在callback/errback鏈中「路由」。(你能夠將各個回調看做成路由器,而後根據傳入參數的狀況來決定其返回值進入下一個stage的哪條鏈,或者說控制權進入下一個stage的哪一個類型的回調)。雖然示例程序是虛構出來的,但它揭示了控制權在deferred的回調鏈中交錯傳遞具體方向依賴於返回值的類型。
那咱們是否是已經對deferred無所不知了?不,咱們還會在下面的部分繼續講解deferred的更多的功能。但在第十一部分,咱們先不講這部份內容,而是實現咱們的Twisted版本的詩歌下載服務器。