从subprocess.call捕获我无法控制的输出

时间:2012-06-02 08:32:15

标签: python subprocess stringio

我正在测试一段使用subprocess.call()的Python代码,所以我无法控制该函数调用。我需要捕获系统调用的输出来做断言。我试图将os.stdout设置为StringIO对象,但这并不能捕获系统调用输出。我该如何解决这个问题?到目前为止,这是我的代码:

要测试的代码(我无法控制):

def runFile(filename):
    check_call("./" + filename)

我尝试捕获系统调用输出:

import StringIO
oldout = sys.stdout
try:
    sys.stdout = StringIO.StringIO()
    runFile("somefile")
    output = sys.stdout.getvalue()
    assert output == "expected-output"
finally:
    sys.stdout = oldout

2 个答案:

答案 0 :(得分:1)

您可以通过

重定向自己的标准输出
rd, wr = os.pipe()
oldstdout = os.dup(1)
os.dup2(wr, 1)
os.close(wr)

然后在外部进程运行时从rd读取。

这可能不起作用,因为外部进程可能会在管道缓冲区中写入更多内容。在这种情况下,你必须产生一个阅读线程。

然后,使用

恢复旧状态
os.dup2(oldstdout, 1)
os.close(oldstdout)
os.close(rd)

并继续正常。

答案 1 :(得分:0)

subprocess模块使用os.write直接写入输出流,因此更改sys.stdout将无法执行任何操作。

这就是为什么你通常需要使用...([cmd, arg, ...], stdout=output_file, ...)表示法指定与子进程模块一起使用的输出流,以及为什么你不能使用StringIO作为输出文件,因为它没有要写的fileno到。

因此,在导入您需要测试的模块之前,实现您想要做的事情的唯一方法是使用monkeypatch subproces.check_call,以便它更像check_output

例如:

import subprocess

def patched_call(*popenargs, **kwargs):
    if 'stdout' in kwargs:
        raise ValueError('stdout argument not allowed, it will be overridden.')
    process = subprocess.Popen(*popenargs, stdout=subprocess.PIPE, **kwargs)
    output, unused_err = process.communicate()
    # do something with output here
    retcode = process.poll()
    if retcode:
        cmd = kwargs.get("args")
        if cmd is None:
            cmd = popenargs[0]
        raise CalledProcessError(retcode, cmd, output=output)
    return retcode

subprocess.check_call = patched_call