Python Popen - wait vs communication vs CalledProcessError

时间:2015-06-22 14:15:03

标签: python python-2.7 error-handling popen

继续from my previous question我看到要获取我在python中通过Popen生成的进程的错误代码我必须调用wait()或communic()(可以用来访问Popen stdout和stderr)属性):

app7z = '/path/to/7z.exe'
command = [app7z, 'a', dstFile.temp, "-y", "-r", os.path.join(src.Dir, '*')]
process = Popen(command, stdout=PIPE, startupinfo=startupinfo)
out = process.stdout
regCompressMatch = re.compile('Compressing\s+(.+)').match
regErrMatch = re.compile('Error: (.*)').match
errorLine = []
for line in out:
    if len(errorLine) or regErrMatch(line):
        errorLine.append(line)
    if regCompressMatch(line):
        # update a progress bar
result = process.wait() # HERE
if result: # in the hopes that 7z returns 0 for correct execution
    dstFile.temp.remove()
    raise StateError(_("%s: Compression failed:\n%s") % (dstFile.s, 
                       "\n".join(errorLine)))

但是the docs警告wait()可能会死锁(当stdout = PIPE时,就是这里的情况),而communicate()可能会溢出。所以:

  1. 这里使用的是什么?请注意,我确实使用输出
  2. 我应该如何使用沟通?它会是:

    process = Popen(command, stdout=PIPE, startupinfo=startupinfo)
    out = process.communicate()[0]
    # same as before...
    result = process.returncode
    if result: # ...
    

    不确定阻止和内存错误

  3. 处理问题的更好/更pythonic方式?我不认为subprocess.CalledProcessError or the subprocess.check_call/check_output适用于我的情况 - 或者是吗?
  4. 免责声明:我没有编写代码,我是当前的维护者,因此问题3。

    相关:

    如果这有所不同,我在Windows上 - python 2.7.8

    应该有一个 - 最好只有一个 - 显而易见的方法

1 个答案:

答案 0 :(得分:10)

  • 关于死锁:您可以安全地使用stdout=PIPEwait()一起 iff 从管道中读取。 .communicate()执行阅读并为您致电wait()
  • 关于内存:如果输出可以无限制,则不应使用在内存中累积所有输出的.communicate()
  

在这里使用什么是正确的?

要启动子进程,请逐行读取其输出并等待它退出:

#!/usr/bin/env python
from subprocess import Popen, PIPE

process = Popen(command, stdout=PIPE, bufsize=1)
with process.stdout:
    for line in iter(process.stdout.readline, b''): 
        handle(line)
returncode = process.wait() 

由于有限的OS管道缓冲区,此代码不会死锁。此外,代码支持无限输出的命令(如果单个行适合内存)。

iter()用于在子流程'之后立即读取一行。刷新stdout缓冲区,以解决方法the read-ahead bug in Python 2。如果您在编写行时不需要读取行而不等待缓冲区填充或子进程结束,则可以使用简单的for line in process.stdout。请参阅Python: read streaming input from subprocess.communicate()

如果知道命令输出在所有情况下都可以适合内存,那么你可以一次获得输出:

#!/usr/bin/env python
from subprocess import check_output

all_output = check_output(command)

如果命令以非零退出状态返回,则引发CalledProcessError。在内部,check_output() uses Popen() and .communicate()

  

应该有一个 - 最好只有一个 - 显而易见的方法

subprocess.Popen()是在很多情况下都可以使用的主要API。对于常见用例,有Popen.communicate()check_output()check_call()等便捷功能/方法。

有多种方法,功能因为有多种不同的用例。