XML – 小心陌生人

這一篇是Mark Pilgrim在2004/2/18寫的Beware of strangers中譯心得版,Mark Pilgrim現在在Google公司服務,看起來Google好像很喜歡用Python,所以可以好好讀一下這篇文章。

Universal Feed Parser 3.0 beta 18已經過時了,假如可以的話就用libxml2,Libxml2使用libiconv 來做大型的字元編碼轉換,包括中文,日文跟韓文等的編碼,其結果就是Universal Feed Parser會變得更普遍。

用libxml2來寫程式就像是異國的陌生人給你的一個扣人心弦的擁抱那樣,它似乎有可能滿足您的最瘋狂的夢想,但事好像在你的腦袋某個地方有個揮之不去的聲音警告你,你將糟糕地受騙上當。

Libxml2很快,我的意思是瘋狂的快,沒有別人可以接近它,它超級的快並且超級的符合它所宣稱支援所有的規格,它越來越快的同時,功能也越來越多,所以你只要知道在某個地方,有些人在出賣他們自己的靈魂,而你只會希望那不會是你。

這裡有一個真實故事:

對於Universal Feed Parser我有一個龐大而且越來越大的單元測試集,俺在很多種平台和條件下執行:Windows、OS X、Debian GNU/Linux、Python 2.1、Python 2.2、Python 2.3、Python的PyXML、Python的libxml2、Python的XML內建解析器,測試的架構已經很複雜,現在所要做的事情之一就是示範一個本地的HTTP伺服器來測試HTTP層跟XML層的相互影響(像是字元編碼),HTTP伺服器檢查自己的feed測試來拉出包含回應的自訂HTTP表頭的列表,它比我想的到的還要可怕。

今晚早些時候,我整合了libxml2支援, 當我用Windows下Python 2.3的libxml2 beta 18版跑了整套的單元測試,一個測試失敗了,它是獲取透過測試架構的HTTP伺服器的路由的一個測試,這失敗使它得很困難並且大大地增加了造成的問題比要測試的程式碼更多的事情。

拿掉libxml2,所有的測試就會通過,放回libxml2,相同的測試就會失敗,真是奇怪。

開啟my _debug旗標再重試那一個測試,它會印出一堆東西到stderr然後就… 通過了,關掉libxml2,第二次再回去試,難以置信的可以通過,關掉偵錯,重新執行那個測試,它還是可以通過,在它自己執行的時候這測試一直可以通過。

OK.測試的問題在大概#1100的某些地方,超過了2000個左右的測試,執行測試的前半部粉,他們通過了,就跟之前的一樣,執行測試的第二部份,他們全通過了,包括之前失敗的那一個,將所有的測試合起來執行︰就失敗了,關掉libxml2︰所有的測試都可以過,很詭異吧!

花時間來深入挖掘,它到底是哪裡錯誤?它不只是失敗;它實際上是掛了一會兒,然後失敗,所有的測試通常會在一秒鐘之內通過或失敗;在我用了四年多的筆電上,我可以用23秒執行2000個測試,除了這一個,它掛了 10秒鐘,然後失敗,呣,10秒鐘,feed解析器提出了一個10秒鐘的網路逾時,增加逾時值,瞧,現在它掛了20秒,然後失敗,我自訂的HTTP伺服器沒有回應,但是,只有在一個測試上,只有當我使用libxml2,增加了這困擾的聲音。

記住我的HTTP伺服器沒有呼叫libxml2,它只能從磁碟讀取檔案,或一個或兩個正規表示式的比對,然後印出原始資料,libxml2後來要晚一點才放進來。

是再次打開 _debug旗標的時候了,為了避免自己迷失在雪片般的廢話中,我增加了程式碼假如檔案名稱跟問題中的測試匹配就設定_debug = 1,重跑所有的測試並關掉libxml2作為控制,所有的測試通過,而且我抓了一個測試的偵錯資訊,太棒了,開啟libxml2,重跑,然後… 所有的測試通過。

仔細檢查程式碼;除了print敘述外從來就不會有_debug == 1的情形會發生,這個測試會在正常的情況下失敗,但是當你偵錯的時候卻可以通過,真是詭異,奇怪吧!

我很內疚的決定了移掉問題中的測試以及beta 18版的rm, cvs remove, cvs commit,希望沒有人會通知,嘿,這是測試版說,移掉偵錯碼,重跑整套的測試,所有的測試會通過… 除了測試現在是在我剛移掉那段程式碼的相同位置(#1100)。

OK,這一定是潛伏在測試架構中的很難找出的錯誤,(但是如果是這樣,為什麼libxml2會這樣不同呢?問問這惱人的聲音), HTTP伺服器是設定來服務一定數量的請求跟自我中斷,或許我有一個離一誤差(off-by-1 error)而且它會在服務最後一個測試前中斷,或者可能是沒有做好初始化,然後在第一個相關的HTTP測試時失敗,刺破了HTTP-相關測試的名單;沒有一個假設是對的,這個測試過去在#1101的時候可以通過,現在是在#1100的時候失敗,而且它跟先前我刪去地方的錯誤是一樣的,(很明確的,我覺得非常非常的內疚),不知何故,也不知哪個地方,在之間幾百個請求中libxml2會讓我的http伺服器失敗那麼一次。

再一次開啟 _debug = 1 的程式碼重跑,註解這個真錯得程式碼重跑,突然地又全過了,仔細檢查;是的,libxml2是啟用的,不,這不是我的幻覺,再重跑一次,全過,重開機,全過,我有做什麼不同的事嗎?我仔細的搜尋翻找,最後我看到了:我留了一個全域的_debug宣告沒有註解起來,把它註解起來;就錯誤,移掉註解,就又通過。

請注意只是想說在函式內的全域的_debug應該一點都不會有影響才對,假如你從沒在函式內設定_debug的話,它就像你從來沒有實行過的威脅一樣,這個程式碼就像海中的碎片一樣,然而它就是這樣:把它註解,失敗,宣告,成功,開啟libxml2,失敗,關掉libxml2,成功。

這個故事告訴我們:libxml2會導致無關的子系統失敗,除非你威脅它要進行偵錯

Postscript: 我決定在這個警告下釋放它,但是之後我發現在Python 2.1下有一個不相干的錯誤(而且不難理解,在修正了之後重跑測試,現在我已經不能再重新產生我所提到的錯誤了,我不斷的設定全域的_debug_debug = 1 以及開關libxml2數次以嘗試把這個非常錯誤的行為帶回,都無濟於事。

我發誓這曾發生過,這不是我的幻覺,但是我承認我不能證明給你看,你只能聽我說的話,在這時候,我的所有測試都可以通過,所以我要今晚停止寫程式並且在其他事情發生前釋放這個討厭的東西,使用但請自擔風險,並提防陌生人。

這篇看完之後,真是心有慼慼焉,常常碰到這種詭異的事,可是又無解,只好放手不管,把它交給神,過些時日,可能就不會有問題了,但是還是對自己的程式技術沒有長進,唉!真是辛苦的工作!

感謝你看到這裡,很快就可以離開了,但最好的獎勵行動就是按一下幫我分享或留言,感恩喔~

點我分享到Facebook

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *