2016-08-22 69 views
1

我有一些使用requests庫發出請求的Python代碼,偶爾會遇到IncompleteRead錯誤。我試圖更新這個代碼來更好地處理這個錯誤,並想測試它的工作原理,所以我想知道如何實際觸發IncompleteRead的條件。如何在Python Web應用程序中觸發IncompleteRead(有意)?

我意識到我可以在單元測試中做一些嘲諷;我只是想真實地重現以前發生此錯誤的情況(如果可以的話),並確保我的代碼能夠正確處理它。

+0

你可以發送EOF或其他東西嗎? –

回答

1

當測試依賴於外部行爲的代碼(例如服務器響應,系統傳感器等)時,通常的方法是僞造外部因素而不是工作來產生它們。

創建您用來發出HTTP請求的函數或類的測試版本。如果您在整個代碼庫中直接使用requests,請停止:直接耦合到庫和外部服務是非常難以測試

你提到你要確保你的代碼可以處理這個異常,並且你寧願避免嘲笑這個原因。 只要你打包你需要的模塊來模擬你的代碼庫,模擬也是安全的。如果你不能模擬測試,那麼你的設計中缺少圖層(或者要求太多的測試套件)。

因此,舉例來說:

class FooService(object): 
    def make_request(*args): 
     # use requests.py to perform HTTP requests 
     # NOBODY uses requests.py directly without passing through here 

class MockFooService(FooService): 
    def make_request(*args): 
     raise IncompleteRead() 

第2類是專爲測試這種特殊情況下的目的編寫的測試工具。隨着測試的覆蓋範圍和完整性不斷增加,您可能需要更復雜的語言(以避免不斷進行子類化和重複),但通常最好從最簡單的代碼開始,它可以輕鬆讀取並測試所需的情況。

+0

你的建議是合理的;我們一直這樣做。但特別是在'IncompleteRead'的情況下,有些操作系統級別的事情發生,我覺得我們並不完全理解。爲了提供更多的背景知識:在生產中發生此錯誤時,我的團隊將看到其他進程受到影響時的連鎖反應。因此無論是內存損壞事件還是網絡I/O事件或其他事情......如果我能夠重現* actual *錯誤而不是簡單地在虛假中提出正確的異常,它會讓我感覺好多了。 –

+0

我明白了。聽起來像是一個很難調試的場景。如果這會影響跨I/O邊界的系統,我仍然認爲第一步是使用mock通過強大的測試來確保系統中的每個角色都可以對此作出反應並從中恢復。一旦你擁有了這個基礎架構,你可以a)感到安全,b)開始從代碼庫中收集有關故障的信息,c)通過重試和回退來處理故障。這不會解決問題,但它將有助於處理它。至於找到根本原因,我認爲你需要OS級工具 – slezica

+0

我同意我們應該使用mock在單元級別測試代碼。對我來說,這不是一個或者一件事。我也同意我們應該收集更多信息。我只是希望我知道究竟是什麼原因導致'IncompleteRead';如果我知道在將新代碼投入生產之前我可以在本地重現該場景。 –

1

添加第二個答案,這次更重要。我深入瞭解了一些源代碼,並發現可能有幫助的信息

IncompleteRead異常從httplib冒泡,它是Python標準庫的一部分。最有可能的,它來自this function

def _safe_read(self, amt): 
    """ 
    Read the number of bytes requested, compensating for partial reads. 
    Normally, we have a blocking socket, but a read() can be interrupted 
    by a signal (resulting in a partial read). 

    Note that we cannot distinguish between EOF and an interrupt when zero 
    bytes have been read. IncompleteRead() will be raised in this 
    situation. 

    This function should be used when <amt> bytes "should" be present for 
    reading. If the bytes are truly not available (due to EOF), then the 
    IncompleteRead exception can be used to detect the problem. 
    """ 

因此,HTTP響應被消耗之前或者套接字已關閉,或閱讀器試圖讓太多的字節出來。通過搜索結果來判斷(所以用一點鹽就可以了),沒有其他的奧祕情況可以做到這一點。

第一種情況可以用strace進行調試。如果我正確地讀這篇文章,該第二場景可由requests模塊引起的,如果:

  • Content-Length標頭存在超過由所述服務器發送的數據的實際量。
  • 分塊響應被錯誤地組裝(在其中一個塊之前有錯誤的長度字節),或者常規響應被解釋爲分塊。

此功能提高了Exception

def _update_chunk_length(self): 
    # First, we'll figure out length of a chunk and then 
    # we'll try to read it from socket. 
    if self.chunk_left is not None: 
     return 
    line = self._fp.fp.readline() 
    line = line.split(b';', 1)[0] 
    try: 
     self.chunk_left = int(line, 16) 
    except ValueError: 
     # Invalid chunked protocol response, abort. 
     self.close() 
     raise httplib.IncompleteRead(line) 

嘗試檢查Content-Length頭的緩衝反應的,或者你的分塊響應的塊格式。

農產品錯誤,請嘗試:

  • 在一個chunk
  • 關閉的開始強制無效Content-Length
  • 使用分塊響應協議,具有過大的長度字節插座中間響應
相關問題