Python 筛选具有潜在未安装输出的进程输出,检测退出代码和X之后的超时

Python 筛选具有潜在未安装输出的进程输出,检测退出代码和X之后的超时,python,django,subprocess,uwsgi,Python,Django,Subprocess,Uwsgi,我有一些现有的Django代码在uwsgi(在Linux下)下运行,禁用了线程功能,它为一些请求执行一个我无法控制的子进程 正常操作如下: 子流程运行的时间很短,并返回退出代码0或其他内容。代码将向stdout/stderr写入一些消息。返回代码(退出代码)将告诉我工作是否正确完成。如果执行失败,最好收集stdout/stderr并记录它,以了解失败的原因 然而,在极少数情况下,子流程可能会遇到迄今为止尚未理解的竞争条件,并将执行以下操作 它将反复向stdout和stderr写入特定消息,

我有一些现有的Django代码在uwsgi(在Linux下)下运行,禁用了线程功能,它为一些请求执行一个我无法控制的子进程

正常操作如下:

  • 子流程运行的时间很短,并返回退出代码0或其他内容。代码将向stdout/stderr写入一些消息。返回代码(退出代码)将告诉我工作是否正确完成。如果执行失败,最好收集stdout/stderr并记录它,以了解失败的原因
然而,在极少数情况下,子流程可能会遇到迄今为止尚未理解的竞争条件,并将执行以下操作

  • 它将反复向stdout和stderr写入特定消息,并循环并永久挂起
因为我不知道是否存在任何其他竞争条件,这可能会冻结有或没有任何输出的进程。Id'还想添加一个超时。(虽然这是一个解决方案,但是获取返回代码并检测重复的消息已经是一个很好的成就

到目前为止,我尝试的是:

import os
import select
import subprocess
import time

CMD = ["bash", "-c", "echo hello"]
def run_proc(cmd=CMD, timeout=10):
    """ run a subprocess, fetch (and analyze stdout / stderr) and
        detect if script runs too long
        and exit when script finished
    """

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = proc.stdout.fileno()
    stderr = proc.stderr.fileno()

    t0 = time.time()
    while True:
        if time.time() - t0 > timeout:
            print("TIMEOUT")
            break
        rc = proc.returncode
        print("RC", rc)
        if proc.returncode is not None:
            break
        to_rd, to_wr, to_x = select.select([stdout, stderr], [], [], 2)
        print(to_rd, to_wr, to_x)
        if to_rd:
            if stdout in to_rd:
                rdata = os.read(stdout, 100)
                print("S:", repr(rdata))
            if stderr in to_rd:
                edata = os.read(stderr, 100)
                print("E:", repr(edata))
    print(proc.returncode)
事实上,我不需要分别处理stdout和stderr,但这并没有改变任何事情

然而,当子流程完成其输出时,发生了一些非常奇怪的事情

the output of select tells me, that stdout and stderr can be read from, but when I read I get an empty string.
proc.returncode is still None
如何修复上述代码,或者如何以不同的方式保存问题?

请至少检查:

输出:


非常感谢。
proc.poll()
对我有帮助。但是
while proc.poll()是None,proc.returncode是None:
我想可以改成
while proc.poll():
或者改成
while True:
后面跟着
rc=proc.poll()
,如果rc不是None,则在某个地方出现一个
:break
另一个问题。
proc.terminate()
似乎会留下一个僵尸。任何避免这种情况的方法。由于uwsgi运行了很长一段时间,理论上有可能会留下相当多的僵尸
proc.terminate()
,然后是
proc.wait()
似乎解决了僵尸问题。我已经给出了您的答案。请调整您的答案,以便调用
proc.terminate()
proc.wait()
,或者更改while语句,使
proc.poll()的结果更清楚
proc.returncode
或者如果您愿意,我可以将这些更改添加到您的答案中,然后将此答案标记为solution@gelonida:请编辑我的答案。
def run_proc(cmd=CMD, timeout=10):
    """ run a subprocess, fetch (and analyze stdout / stderr) and
        detect if script runs too long
        and exit when script finished
    """

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = proc.stdout.fileno()
    stderr = proc.stderr.fileno()

    t0 = time.time()
    while True:
        returncode = proc.poll()
        print("RC", returnode)
        if returncode is not None:
            break

        if time.time() - t0 > timeout:
            print("TIMEOUT")
            # You need to kill the subprocess, break doesn't stop it!
            proc.terminate()
            # wait for the killed process to 'reap' the zombie
            proc.wait()
            break

        to_rd, to_wr, to_x = select.select([stdout, stderr], [], [], 2)
        print(to_rd, to_wr, to_x)
        if to_rd:
            if stdout in to_rd:
                rdata = os.read(stdout, 100)
                print("S:", repr(rdata))
            if stderr in to_rd:
                edata = os.read(stderr, 100)
                print("E:", repr(edata))
    print(returncode)
RC None
[3] [] []
S: b'hello\n'
RC None
[3, 5] [] []
S: b''
E: b''
0