使用 Python 的 subprocess.Popen 时的异常处理

Exception handling when using Python's subprocess.Popen

在处理打开的文件时,Python 具有 with 语法,可确保文件在离开块时关闭 - 无论异常等如何。

with open('foo.txt') as f:
    foo = f.read()

由于进程也是资源,我想知道:使用 Popen 时是否可能或推荐类似的东西?例如,在 finally 子句中 Popen.kill(); Popen.communicate() 应该是 运行 - 假设我不介意在进程完成之前阻塞?

从Python3.2开始Popen是上下文管理器。

来自 docs:

Popen objects are supported as context managers via the with statement: on exit, standard file descriptors are closed, and the process is waited for.

这应该可以满足您的需求。

这是来自标准库 subprocess.py 的相关部分 在 Python 3.4:

def __enter__(self):
    return self

def __exit__(self, type, value, traceback):
    if self.stdout:
        self.stdout.close()
    if self.stderr:
        self.stderr.close()
    if self.stdin:
        self.stdin.close()
    # Wait for the process to terminate, to avoid zombies.
    self.wait()

现在您可以在 Python 2.7

from subprocess import Popen

class MyPopen(Popen):

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        if self.stdout:
            self.stdout.close()
        if self.stderr:
            self.stderr.close()
        if self.stdin:
            self.stdin.close()
        # Wait for the process to terminate, to avoid zombies.
        self.wait()

if __name__ == '__main__':
    with MyPopen(['ls']) as p:
        print(p)

您可以向任何 class 添加两个自定义方法以实现与 with 语句的兼容性。

class CustomObject(object):
    def __enter__(self):
        """ This method execudes when entering block. """
        return thing_you_want_to_use

    def __exit__(self, type, value, traceback):
        """ This method execudes on block exit. """
        # Tear things down.

对于 2.7,您还可以使用 @contextlib.contextmanager:

import contextlib

@contextlib.contextmanager
def manage_process(process):
    try:
        yield process
    finally:
        for stream in [process.stdout, process.stdin, process.stderr]:
            if stream:
                stream.close()
        process.wait()

例如:

with manage_process(Popen(['ls'])) as p:
    print(p)