在中包装urllib3.HTTPResponseio.TextIOWrapp

2024-03-28 22:25:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用AWSboto3库,它返回一个urllib3.response.HTTPResponse的实例。该响应是io.IOBase的子类,因此表现为二进制文件。它的read()方法返回bytes个实例。在

现在,我需要从以这种方式接收的文件中解码csv数据。我希望我的代码可以同时在py2py3上工作,因此我使用backports.csv,它依赖于io.IOBase对象作为输入,而不是py2的file()对象。在

第一个问题是HTTPResponse为CSV文件生成bytes数据,我有{},它需要{}数据。在

>>> import io
>>> from backports import csv  # actually try..catch statement here
>>> from mymodule import get_file

>>> f = get_file()  # returns instance of urllib3.HTTPResponse
>>> r = csv.reader(f)
>>> list(r)
Error: iterator should return strings, not bytes (did you open the file in text mode?)

我试图用io.TextIOWrapper包装HTTPResponse,但得到了错误'HTTPResponse' object has no attribute 'read1'。这是预期的,因为TextIOWrapper用于BufferedIOBase对象,而不是{}对象。它只发生在python2TextIOWrapper的实现上,因为它总是期望底层对象具有read1source),而{}的实现检查read1是否存在,并优雅地返回到readsource)。在

^{pr2}$

然后我试图用io.BufferedReader包装HTTPResponse,然后用io.TextIOWrapper包装。我得到了以下错误:

>>> f = get_file()
>>> br = io.BufferedReader(f)
>>> tw = io.TextIOWrapper(br)
>>> list(csv.reader(f))
ValueError: I/O operation on closed file.

经过一番调查发现,只有当文件不是以\n结尾时才会发生错误。如果它以\n结尾,那么问题就不会发生,一切都正常。在

HTTPResponsesource)中有一些关闭底层对象的附加逻辑,这似乎是导致问题的原因。在

问题是:如何将代码写入

  • 同时使用python2和python3,最好不要尝试..catch或依赖于版本的分支
  • 正确处理表示为HTTPResponse的CSV文件,而不管它们是否以\n结尾?在

一种可能的解决方案是在TextIOWrapper周围创建一个自定义包装器,当对象关闭时,read()返回{},而不是引发{}。但是,没有这样的黑客攻击,还有更好的解决方案吗?在


Tags: 文件csv数据对象ioimportsourceread
1条回答
网友
1楼 · 发布于 2024-03-28 22:25:16

看起来这是urllib3.HTTPResponsefile对象之间的接口不匹配。它在this ^{} issue #1305中描述。在

目前还没有修复程序,因此我使用了下面的包装器代码,看上去效果不错:

class ResponseWrapper(io.IOBase):
    """
    This is the wrapper around urllib3.HTTPResponse
    to work-around an issue shazow/urllib3#1305.

    Here we decouple HTTPResponse's "closed" status from ours.
    """
    # FIXME drop this wrapper after shazow/urllib3#1305 is fixed

    def __init__(self, resp):
        self._resp = resp

    def close(self):
        self._resp.close()
        super(ResponseWrapper, self).close()

    def readable(self):
        return True

    def read(self, amt=None):
        if self._resp.closed:
            return b''
        return self._resp.read(amt)

    def readinto(self, b):
        val = self.read(len(b))
        if not val:
            return 0
        b[:len(val)] = val
        return len(val)

使用方法如下:

^{pr2}$

类似的修复是由urllib3维护人员在bug报告评论中提出的,但这将是一个突破性的改变,因此目前情况可能不会改变,所以我不得不使用wrapper(或者做一些猴子补丁,这可能更糟)。在

相关问题 更多 >