捕获标准输出时 python2 和 python3 之间的 StringIO 可移植性
StringIO portability between python2 and python3 when capturing stdout
我写了一个 python 包,我设法使它与 python 2.7 和 python 3.4 完全兼容,但有一个例外一直困扰着我。该软件包包含一个命令行脚本,在我的单元测试中,我使用此代码 运行 脚本的主例程,同时覆盖 sys.argv 以传递 argparse 的命令行参数,并捕获脚本的标准输出以进行比较:
@contextlib.contextmanager
def runmain(mainfunction, arglist):
"""Run mainfunction with arglist in sys.srgv, and capture stdout."""
origargv, sys.argv = sys.argv, arglist
origout, sys.stdout = sys.stdout, io.StringIO()
rtn = mainfunction()
sys.stdout.seek(0)
yield (rtn, sys.stdout.read())
sys.stdout = origout
sys.argv = origargv
class test_imdutil_main(unittest.TestCase):
def test_help(self):
"""Test -h option."""
with runmain(imdutil_main, ['imdutil.py', '-h']) as (rtn, capture):
# do stuff with rtn and capture...
这在 python 3.4 中运行良好,但在 python 2.7 中它会产生错误:
TypeError: unicode argument expected, got 'str'
我还没有找到一种方法来从 python 2.7 和 python 3.4 之间可移植的任意函数捕获标准输出。
顺便说一句,我不得不承认我根本不了解装饰、上下文管理器或 "yield" 关键字。我的 运行main() 函数的灵感来自:
http://schinckel.net/2013/04/15/capture-and-test-sys.stdout-sys.stderr-in-unittest.testcase/
顺便说一句,我的代码来源完整包在这里:
https://github.com/NF6X/pyImageDisk
目前,由于这个问题,它的单元测试在 python 2.7 下被部分破坏。谁能帮我弄清楚如何以可移植的方式解决这个 stdout 重定向问题,pythonic 方式,最好不要添加任何更多的外部依赖项?
你试过了吗? (可以留在你的代码下 Python 3.x)
from __future__ import unicode_literals
否则我的代码中有什么可以在使用 io.StringIO
时使其兼容:
f = io.StringIO(datafile.read().decode('utf-8'), newline=None)
然后查看您的代码:
yield (rtn, sys.stdout.read())
可以改为:
yield (rtn, sys.stdout.read().decode('utf-8'))
您将 Python 2 bytes-only sys.stdout
替换为仅采用 Unicode 的代码。您必须在此处调整 Python 版本的策略,并使用不同的对象:
try:
# Python 2
from cStringIO import StringIO
except ImportError:
# Python 3
from io import StringIO
并删除上下文管理器中的 io.
前缀:
origout, sys.stdout = sys.stdout, StringIO()
cStringIO.StringIO
对象是 io.BytesIO
的 Python 2 等价物;它要求您编写纯字节串,而不是 unicode
对象。
你也可以在Python2中使用io.BytesIO
,但是你想测试sys.stdout
是否是一个io.TextIOBase
subclass;如果不是,则用二进制 BytesIO
对象替换对象,否则使用 StringIO
对象:
import io
if isinstance(sys.stdout, io.TextIOBase):
# Python 3
origout, sys.stdout = sys.stdout, io.StringIO()
else:
# Python 2 or an unorthodox binary stdout setup
origout, sys.stdout = sys.stdout, io.BytesIO()
我写了一个 python 包,我设法使它与 python 2.7 和 python 3.4 完全兼容,但有一个例外一直困扰着我。该软件包包含一个命令行脚本,在我的单元测试中,我使用此代码 运行 脚本的主例程,同时覆盖 sys.argv 以传递 argparse 的命令行参数,并捕获脚本的标准输出以进行比较:
@contextlib.contextmanager
def runmain(mainfunction, arglist):
"""Run mainfunction with arglist in sys.srgv, and capture stdout."""
origargv, sys.argv = sys.argv, arglist
origout, sys.stdout = sys.stdout, io.StringIO()
rtn = mainfunction()
sys.stdout.seek(0)
yield (rtn, sys.stdout.read())
sys.stdout = origout
sys.argv = origargv
class test_imdutil_main(unittest.TestCase):
def test_help(self):
"""Test -h option."""
with runmain(imdutil_main, ['imdutil.py', '-h']) as (rtn, capture):
# do stuff with rtn and capture...
这在 python 3.4 中运行良好,但在 python 2.7 中它会产生错误:
TypeError: unicode argument expected, got 'str'
我还没有找到一种方法来从 python 2.7 和 python 3.4 之间可移植的任意函数捕获标准输出。
顺便说一句,我不得不承认我根本不了解装饰、上下文管理器或 "yield" 关键字。我的 运行main() 函数的灵感来自:
http://schinckel.net/2013/04/15/capture-and-test-sys.stdout-sys.stderr-in-unittest.testcase/
顺便说一句,我的代码来源完整包在这里:
https://github.com/NF6X/pyImageDisk
目前,由于这个问题,它的单元测试在 python 2.7 下被部分破坏。谁能帮我弄清楚如何以可移植的方式解决这个 stdout 重定向问题,pythonic 方式,最好不要添加任何更多的外部依赖项?
你试过了吗? (可以留在你的代码下 Python 3.x)
from __future__ import unicode_literals
否则我的代码中有什么可以在使用 io.StringIO
时使其兼容:
f = io.StringIO(datafile.read().decode('utf-8'), newline=None)
然后查看您的代码:
yield (rtn, sys.stdout.read())
可以改为:
yield (rtn, sys.stdout.read().decode('utf-8'))
您将 Python 2 bytes-only sys.stdout
替换为仅采用 Unicode 的代码。您必须在此处调整 Python 版本的策略,并使用不同的对象:
try:
# Python 2
from cStringIO import StringIO
except ImportError:
# Python 3
from io import StringIO
并删除上下文管理器中的 io.
前缀:
origout, sys.stdout = sys.stdout, StringIO()
cStringIO.StringIO
对象是 io.BytesIO
的 Python 2 等价物;它要求您编写纯字节串,而不是 unicode
对象。
你也可以在Python2中使用io.BytesIO
,但是你想测试sys.stdout
是否是一个io.TextIOBase
subclass;如果不是,则用二进制 BytesIO
对象替换对象,否则使用 StringIO
对象:
import io
if isinstance(sys.stdout, io.TextIOBase):
# Python 3
origout, sys.stdout = sys.stdout, io.StringIO()
else:
# Python 2 or an unorthodox binary stdout setup
origout, sys.stdout = sys.stdout, io.BytesIO()