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