如何打包打开的二进制流 - Python 2 file
,Python 3 io.BufferedReader
,io.BytesIO
- io.TextIOWrapper
?使用io.TextIOWrapper打包流打印
我想编写代码,将工作不变:
- 运行于Python的2
- 运行在Python 3的
- 与标准库生成的二进制流(即我可以无法控制它们是什么类型)
- 使二进制流成为测试双打(即没有文件句柄,无法重新打开)。
- 生成包装指定流的
io.TextIOWrapper
。
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"))
在Python 3的标准库创建的流上工作正常。相同的代码,但是,无法在被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
一个明显的响应是具有其中测试是否流实际上代码中的分支是一个Python 2 file
对象,并且与io.*
对象的处理方式不同。
这不是井测试的代码的选项,因为它使单元测试的一个分支 - 这,为了尽可能快地运行,绝不能造成任何真正文件系统对象 - 不能运动。
单元测试将提供测试双打,而不是真正的file
对象。因此创建一个不会被那些测试双打执行的分支就是击败测试套件。
不是一个解决方案:io.open
一些答复建议重新开口(例如与io.open
)底层文件句柄:
这两个的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
API。具体来说,它不继承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
。
是什么呢?
所以,我怎么能写代码,无论对于Python 2和Python 3中工作,同时与测试双打和实物,其中周围的包装已经打开字节流的io.TextIOWrapper
?
重:'io.open'你可以改变的单元测试,你知道的,例如一个'tempfile.TemporaryFile()';这当然是一个解决方案的锤子... –
这是一个相当有限的一套限制。例如,单元测试*可以*打开文件,如果这绝对是正确测试某些东西的唯一方法。所以一个可以特殊处理'file'对象来获取文件描述符的包装函数,可以用unittest *进行测试。 –