如何将开放二进制流-Python2 file
、Python3 io.BufferedReader
、io.BytesIO
-包装在io.TextIOWrapper
中
我正在试着写代码,不变地工作:
在Python 2上运行的
io.TextIOWrapper
执行re-open).之所以需要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
https://stackoverflow.com/questions/34447623
复制相似问题