【问题标题】:Capture output from subprocess.call that I have no control over从 subprocess.call 捕获我无法控制的输出
【发布时间】:2012-06-07 07:45:00
【问题描述】:

我正在测试一段使用 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

【问题讨论】:

    标签: python subprocess stringio


    【解决方案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)
    

    然后正常继续。

    【讨论】:

    • 我很喜欢这种方法,但是如果有很多输出,有没有办法让当前进程不阻塞?
    • 如果你不能把函数调用改成check_call(),那就不行。如果您可以更改它,您可以给它一个stdout=subprocess.PIPE 并在另一个线程中轮询stdout,或者您可以将check_call() 替换为Popen、该对象上的stdout.read() 和@987654330 @。但如果你不能改变,这是唯一的选择(除了修补子进程)。
    【解决方案2】:

    subprocess 模块使用os.write 直接写入输出流,因此更改sys.stdout 不会做任何事情。

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

    因此,在导入您需要测试的模块之前,实现您想要做的事情的唯一方法是使用 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
    

    【讨论】:

    • 这似乎是一个很好的解决方案,但它并没有捕获所有输出。这种方法需要为 call() 函数的每个变体提供一个补丁,例如check_call()、output_call()、call()、Popen()等
    猜你喜欢
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多