首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >用io.TextIOWrapper包装一个开放的流

用io.TextIOWrapper包装一个开放的流
EN

Stack Overflow用户
提问于 2015-12-24 13:17:53
回答 6查看 44.6K关注 0票数 42

如何将开放二进制流-Python2 file、Python3 io.BufferedReaderio.BytesIO -包装在io.TextIOWrapper

我正在试着写代码,不变地工作:

在Python 2上运行的

  • 。在Python 3上运行的
  • 。包含从标准库生成的二进制流的
  • (即,我无法控制它们是什么类型)
  • 将二进制流用作双倍测试(即没有文件句柄,无法对包装指定流的io.TextIOWrapper执行re-open).
  • Producing操作。<代码>H215<代码>F216

之所以需要io.TextIOWrapper,是因为标准库的其他部分需要它的API。存在其他类似文件的类型,但没有提供正确的API。

示例

包装表示为subprocess.Popen.stdout属性的二进制流:

import subprocess
import io

gnupg_subprocess = subprocess.Popen(
        ["gpg", "--version"], stdout=subprocess.PIPE)
gnupg_stdout = io.TextIOWrapper(gnupg_subprocess.stdout, encoding="utf-8")

在单元测试中,流被替换为io.BytesIO实例,以控制其内容,而不涉及任何子进程或文件系统。

gnupg_subprocess.stdout = io.BytesIO("Lorem ipsum".encode("utf-8"))

这在Python3的标准库创建的流上工作得很好。然而,同样的代码在Python 2生成的流上失败:

[Python 2]
>>> type(gnupg_subprocess.stdout)
<type 'file'>
>>> gnupg_stdout = io.TextIOWrapper(gnupg_subprocess.stdout, encoding="utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'file' object has no attribute 'readable'

不是解决方案:对file进行特殊处理

一个明显的反应是在代码中有一个分支,用于测试流是否实际上是Python2 file对象,并以与io.*对象不同的方式进行处理。

对于经过良好测试的代码来说,这不是一个选项,因为它会造成单元测试无法执行的分支--为了尽可能快地运行,单元测试不能创建任何真正的文件系统对象。

单元测试将提供测试替身,而不是真正的file对象。因此,创建一个不会被这些测试替身执行的分支是在击败测试套件。

不是一个解决方案:io.open

一些受访者建议重新打开(例如使用io.open)底层文件句柄:

gnupg_stdout = io.open(
        gnupg_subprocess.stdout.fileno(), mode='r', encoding="utf-8")

它同时适用于Python 3和Python 2:

[Python 3]
>>> type(gnupg_subprocess.stdout)
<class '_io.BufferedReader'>
>>> gnupg_stdout = io.open(gnupg_subprocess.stdout.fileno(), mode='r', encoding="utf-8")
>>> type(gnupg_stdout)
<class '_io.TextIOWrapper'>
[Python 2]
>>> type(gnupg_subprocess.stdout)
<type 'file'>
>>> gnupg_stdout = io.open(gnupg_subprocess.stdout.fileno(), mode='r', encoding="utf-8")
>>> type(gnupg_stdout)
<type '_io.TextIOWrapper'>

当然,它的依赖于从它的文件句柄重新打开一个真正的文件。因此,当测试替身是io.BytesIO实例时,它会在单元测试中失败:

>>> gnupg_subprocess.stdout = io.BytesIO("Lorem ipsum".encode("utf-8"))
>>> type(gnupg_subprocess.stdout)
<type '_io.BytesIO'>
>>> gnupg_stdout = io.open(gnupg_subprocess.stdout.fileno(), mode='r', encoding="utf-8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
io.UnsupportedOperation: fileno

不是一个解决方案:codecs.getreader

标准库还有codecs模块,它提供了包装器功能:

import codecs

gnupg_stdout = codecs.getreader("utf-8")(gnupg_subprocess.stdout)

这很好,因为它不会尝试重新打开流。但是它没有提供io.TextIOWrapper接口。具体地说,it io.IOBase不继承encoding ,并且没有属性

>>> type(gnupg_subprocess.stdout)
<type 'file'>
>>> gnupg_stdout = codecs.getreader("utf-8")(gnupg_subprocess.stdout)
>>> type(gnupg_stdout)
<type 'instance'>
>>> isinstance(gnupg_stdout, io.IOBase)
False
>>> gnupg_stdout.encoding
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/codecs.py", line 643, in __getattr__
    return getattr(self.stream, name)
AttributeError: '_io.BytesIO' object has no attribute 'encoding'

所以codecs没有提供可以替代io.TextIOWrapper的对象。

该怎么办呢?

那么,我该如何编写既适用于Python2又适用于Python3的代码,同时使用双重测试和真实对象,即在已经打开的字节流周围包装了一个 io.TextIOWrapper

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34447623

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档