Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 捕获我无法控制的subprocess.call的输出_Python_Subprocess_Stringio - Fatal编程技术网

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

Python 捕获我无法控制的subprocess.call的输出,python,subprocess,stringio,Python,Subprocess,Stringio,我正在测试一段使用subprocess.call()的Python代码,因此我无法控制该函数调用。我需要捕获该系统调用的输出以执行断言。我试图将os.stdout设置为StringIO对象,但这无法捕获系统调用输出。我如何解决这个问题?以下是我目前的代码: 要测试的代码(我无法控制): 我尝试捕获系统调用输出: import StringIO oldout = sys.stdout try: sys.stdout = StringIO.StringIO() runFile("so

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

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

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

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

子流程
模块使用
os.write
直接写入输出流,因此更改
sys.stdout
不会起任何作用

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

因此,实现您想要做的事情的唯一方法是使用monkeypatch
子例程。在导入需要测试的模块之前,请检查\u call
,使其工作起来更像

例如:

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

子流程
模块使用
os.write
直接写入输出流,因此更改
sys.stdout
不会起任何作用

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

因此,实现您想要做的事情的唯一方法是使用monkeypatch
子例程。在导入需要测试的模块之前,请检查\u call
,使其工作起来更像

例如:

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

你可以通过

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)

并正常继续。

您可以通过

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
,也可以用
Popen
、该对象上的
stdout.read()
wait()
替换该
check\u call()
。但是如果您不能更改,它是唯一的选项(除了修补子流程)。我很喜欢这种方法,但是有没有办法使当前流程在有大量输出时不会阻塞?如果您不能将函数调用更改为
check_call()
,则没有。如果您可以更改它,您可以给它一个
stdout=subprocess.PIPE
并在另一个线程中轮询该
stdout
,也可以用
Popen
、该对象上的
stdout.read()
wait()
替换该
check\u call()
。但是如果您不能更改,它是唯一的选择(除了修补子流程)。这似乎是一个好的解决方案,但它不能捕获所有输出。这种方法需要为call()函数的每个变体提供补丁,例如check_call()、output_call()、call()、Popen()等。这似乎是一个不错的解决方案,但它不能捕获所有输出。这种方法需要为call()函数的每个变体都提供补丁,例如check_call()、output_call()、call()、Popen()等。