2012-06-02 27 views
1

我正在測試一段使用subprocess.call()的Python代碼,所以我無法控制該函數調用。我需要捕獲來自該系統調用的輸出來做斷言。我嘗試將os.stdout設置爲一個StringIO對象,但它不捕獲系統調用輸出。我該如何解決這個問題?這裏是我到目前爲止的代碼:捕獲subprocess.call的輸出,我無法控制

代碼來測試(我有過這個沒有控制):

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 

回答

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) 

恢復舊的狀態和正常繼續。

+0

我確實喜歡這種方法,但是有沒有辦法讓當前進程不阻塞,如果有很多輸出? – Addison

+0

如果你不能把函數調用改成'check_call()',那麼不會。如果你可以改變它,你可以給它一個'stdout = subprocess.PIPE'並且在另一個線程中輪詢'stdout',或者你可以用'Popen','stdout.read'替代'check_call() ()'和'wait()'。但如果你不能改變,這是唯一的選擇(除了補丁子進程)。 – glglgl

0

subprocess模塊直接寫入輸出流使用os.write,所以改變sys.stdout不會做任何事情。

這就是爲什麼您通常需要使用...([cmd, arg, ...], stdout=output_file, ...)表示法指定與子流程模塊一起使用的輸出流,以及爲什麼不能使用StringIO作爲輸出文件,因爲它沒有fileno要寫入。

所以導入你需要測試,使其作品更有點像check_output模塊之前達到你想要做的ISTØ猴補丁subproces.check_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 
+0

這似乎是一個很好的解決方案,但它不捕獲所有輸出。這種方法需要爲call()函數的每個變體都有一個補丁,例如, check_call(),output_call(),call(),Popen()等。 – Addison