2016-08-22 87 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
  • 使用分块响应协议,具有过大的长度字节插座中间响应