twisted系列教程十–可以变化的诗
Client 5.0
現在我們將要想我們的client中加入一些變形邏輯.但是首先我不得不說:我不知道怎樣寫一個Byronification 引擎,它超出我的能力范圍了.做為替代,我會實現一個相對簡單的變形–Cummingsifier.Cummingsifier 是可以把一首詩變成令一首cumming風格的詩的算法.下面就是這個算法的實現:
def cummingsify(poem)
????return poem.lower()
不幸的是,這個算法很簡單以至于很難失敗,所以在client 5.0 版本中在 twisted-client-5/get-poetry.py中,我們用了一個可以隨機出現以下結果的算法:
????返回一個正常的結果
????拋出一個GibberishError錯誤
????拋出一個ValueError 錯誤
?
通過這種方法我們模擬了一個有時返回異常的復雜的算法.
在client 5.0 中唯一變化的是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()
當這個程序從server上下載一首詩,還會做下面三件事情中的一件:
????打印出cummingsified 版本的詩
????在輸出原來的詩后然后打印"Cummingsify failed!"
????打印出“The poem download failed.”
盡管我們已經有了從多個server上同時下載詩的能力,但是在測試client 5.0 的時候最好只用一個server,并多運行client 幾次,并試著在不開server的時候運行client.
讓我們畫一下在get_poetry中Deferred的callback/errback 鏈:
圖片十九
注意pass-through errback,它傳遞它接收到的任何的Failure到下一個errback(poem_failed).所以poem_failed 可以處理從get_poetry 和cummingsify 傳過來的錯誤.
讓我們分析一下我們的deferred 的不同的觸發方法.如果我們想要一個正常的正確返回結果,獲取詩的流程應該像圖片二十:
圖片二十
在這種情況下 沒有callback失敗,callback一路向下調用.這時候poem_done 會接收到None作為它的結果,因為got_poem 沒有返回一個值.假如我們想讓后面的callback可以訪問到詩的內容,我們可以修改got_poem 并讓它明確的返回一首詩.
圖片二十一描述了cummingsify 拋出了GibberishError 錯誤的時候:
圖片二十一
因為try_to_cummingsify 拋出了GibberishError 錯誤,poem_failed 被調用,并接收到Failure 對象.
poem_failed 并沒有拋出一個異常,它執行完成后并轉向poem_done.我們讓poem_failed來處理錯誤并返回一個None值是一個合理的行為.另一方面,如果我們讓poem_failed繼續傳遞這個錯誤,那poem_done errback 就會被調用.
注意我們這里got_poem 和poem_failed 永遠不會失敗,所以poem_done errback 永遠不會調用.但是加上它是安全的,因為你不知道got_poem 和poem_failed 有沒有我們不知道的bug. addBoth 保證了這個函數無論deferred如何被觸發都會被調用,addBoth 有點像try/except 中的finally 語句.
現在我們來看一下我們成功下載了一首小詩,但是cummingsify 函數拋出了一個ValueError 的的情況,圖片二十二描述了這個過程:
圖片二十二
圖片二十二跟圖片二十是一樣的,除了got_poem接收的詩是原來的版本而不是已經轉化過的版本.這種變化在try_to_cummingsify callback 中,它用try/except捕捉了ValueError并返回原來版本的詩. deferred 對象一點也感覺不到錯誤的存在.
最后我們用圖片二十三描述一下我們從一個不存在的server上下載一首詩的情況:
圖片二十三
根據以前的流程可知,poem_failed 返回None 所以程序流向下一層的poem_done callback.
Client 5.1
在client 5.0 中,我們用了一個普通的try/except 語句來捕捉在try_to_cummingsify callback 中出現的異常,而不是讓deferred 首先捕捉它.這種方法本身并沒有什么錯誤,但是我們要知道怎么做才是標準的twisted 范.
讓我們假設我們想讓deferred 同時捕捉GibberishError 和 ValueError 異常然后把它們交給errback.為了保證能正確處理異常,我們的errback需要去檢查錯誤類型是否是ValueError,假如是的話,返回原來的詩,所以程序流又能返回到callback 并把原來版本的詩打印出來.
但是這里有一個問題:errback 不會得到原來的詩,它只會得到一個Failure 對象.為了讓errback能處理這個錯誤,我們需要做一些變化讓errback 能接收到原來的詩.
一個修改的方法就是修改cummingsify 函數,讓原來的詩的內容包含在異常中.這個就是我們client 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 的callback/errback 鏈就成下圖了,圖片二十四:
圖片二十四
檢查cummingsify_failed errback:
def cummingsify_failed(err):
????if err.check(CannotCummingsify):
????????print 'Cummingsify failed!'
????????return err.value.args[0]
????return err
我們用了check 方法來檢測綁定在Failure 對象上的異常是否是CannotCummingsify 的實例.假如是的話,我們返回給異常第一個參數并處理這個錯誤.因為返回的不再是一個Failure 對象,程序流轉向callback執行.否則的話我們返回一個Failure 對象,并傳遞給下一層的errback.
圖片二十五描述了當我們遇到一個CannotCummingsify 異常的時候會發生什么:
圖片二十五
所以當我們用deferred 的時候,我們可以選擇是用try/except 去處理異常,還是讓deferred 把錯誤再路由出去.
Summary
在第十部分我們用deferred 路由錯誤的功能來改變我們的client,盡管這個例子不是特別真實的,它確實描述了在一個deferred中怎樣根據callback 和errback返回的結果來控制程序的流程.
現在我們已經知道了deferred 的所有的事情了嗎?還沒有.我們還會繼續講解deferred 在未來的部分.但是我們 先繞個路,在第十一部分,實現我們的twisted poetry server.
總結
以上是生活随笔為你收集整理的twisted系列教程十–可以变化的诗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux的chattr与lsattr命
- 下一篇: Python 获取(字典)字符串时间区间