【发布时间】:2021-01-29 04:14:05
【问题描述】:
我正在尝试从子进程中获取异常。如果我使用.communicate,我可以得到它,但我想避免使用它,因为我正在流式传输来自子进程的输出,并且不想等到整个子进程完成。还假设整个子流程可能需要很长时间。想知道如何在从子进程流式传输标准输出时捕获引发的异常。
考虑下面的例子,所以我想让版本 #1 工作,版本 #2 有点工作,但不希望那样。
在 main.py 中
import subprocess
class ExtProcess():
def __init__(self, *args):
self.proc = subprocess.Popen(['python', *args], stdout=subprocess.PIPE)
def __iter__(self):
return self
def __next__(self):
while True:
line = self.proc.stdout.readline()
if self.proc.returncode:
raise Exception("error here")
if not line:
raise StopIteration
return line
def run():
## version #1
reader = ExtProcess("sample_extproc.py")
for d in reader:
print(f"got: {d}")
## version #2
# proc = subprocess.Popen(['python', "sample_extproc.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# output, error = proc.communicate()
# print("got:", output)
# if proc.returncode:
# raise Exception(error)
def main():
try:
print("start...")
run()
print("complete...")
except Exception as e:
print(f"Package midstream error here: {str(e)}")
finally:
print("clean up...")
if __name__ == "__main__":
main()
在 sample_extproc.py 中
for x in range(10):
print(x)
if x == 3:
raise RuntimeError("catch me")
我想从版本 #1 中获得类似以下内容的输出:
start...
got: b'0\r\n'
got: b'1\r\n'
got: b'2\r\n'
got: b'3\r\n'
Package midstream error here: b'Traceback (most recent call last):\r\n File "sample_extproc.py", line 4, in <module>\r\n raise RuntimeError("catch me")\r\nRuntimeError: catch me\r\n'
clean up...
基本上,它从子进程中遍历标准输出,然后在发生异常时打印异常,然后继续执行清理。
【问题讨论】:
-
程序在退出之前不会返回退出状态。你的子进程在抛出异常时会退出吗?
-
...因此,在您仍在读取数据时尝试检查
returncode没有什么意义。当然,进程可以在退出之前关闭其标准输出,或者在退出时仍有几十个字节在 FIFO 中,但超过几毫秒的情况是非常不寻常的,即使这样,父进程也不会知道退出状态,直到它调用wait()系统调用来检索它(当从僵尸条目中获取退出进程的PID时,它在退出之间的时间留在进程表中调用及其父进程读取该状态)。 -
我确实想问“您的更大目标是什么?”可能适合这里。我喜欢“回答所提出的问题”,但我不禁觉得还有另一种方法可以解决这个问题(取决于它是什么)。
-
...如果您的子进程在看到异常后立即退出,则该异常不会“在中间”,而是在流的末尾,所以您可以去直接从您的
for line in proc.self.stdout:循环退出到p.wait(),并在wait返回后立即检查p.returncode。 -
@CharlesDuffy 抱歉,刚刚明白你提到的内容。所以是的,如果在我循环通过标准输出之后,它实际上可以工作,有 p.wait,然后检查 p.returncode
标签: python subprocess stderr